oh-my-opencode-slim 1.0.7 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +16 -6
  2. package/dist/cli/config-io.d.ts +1 -0
  3. package/dist/cli/index.js +170 -52
  4. package/dist/goal/index.d.ts +3 -0
  5. package/dist/goal/manager.d.ts +41 -0
  6. package/dist/goal/prompts.d.ts +4 -0
  7. package/dist/goal/store.d.ts +15 -0
  8. package/dist/goal/types.d.ts +28 -0
  9. package/dist/hooks/index.d.ts +1 -0
  10. package/dist/hooks/session-goal/index.d.ts +38 -0
  11. package/dist/index.js +1135 -369
  12. package/dist/multiplexer/session-manager.d.ts +3 -1
  13. package/dist/tools/fork/command.d.ts +28 -0
  14. package/dist/tools/fork/files.d.ts +33 -0
  15. package/dist/tools/fork/index.d.ts +10 -0
  16. package/dist/tools/fork/state.d.ts +7 -0
  17. package/dist/tools/fork/tools.d.ts +23 -0
  18. package/dist/tools/fork/vendor.d.ts +28 -0
  19. package/dist/tools/handoff/command.d.ts +29 -0
  20. package/dist/tools/handoff/files.d.ts +33 -0
  21. package/dist/tools/handoff/index.d.ts +10 -0
  22. package/dist/tools/handoff/state.d.ts +7 -0
  23. package/dist/tools/handoff/tools.d.ts +23 -0
  24. package/dist/tools/handoff/vendor.d.ts +28 -0
  25. package/dist/tools/index.d.ts +2 -0
  26. package/dist/tools/subtask/command.d.ts +30 -0
  27. package/dist/tools/subtask/files.d.ts +34 -0
  28. package/dist/tools/subtask/index.d.ts +11 -0
  29. package/dist/tools/subtask/state.d.ts +7 -0
  30. package/dist/tools/subtask/tools.d.ts +23 -0
  31. package/dist/tools/subtask/vendor.d.ts +27 -0
  32. package/dist/tui.js +56 -0
  33. package/dist/utils/session.d.ts +2 -1
  34. package/package.json +1 -1
  35. package/src/skills/clonedeps/README.md +23 -0
  36. package/src/skills/clonedeps/SKILL.md +237 -0
  37. package/src/skills/clonedeps/codemap.md +41 -0
  38. package/src/skills/codemap.md +8 -5
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ var __toESM = (mod, isNodeMode, target) => {
32
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
33
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
34
 
35
- // node_modules/@mozilla/readability/Readability.js
35
+ // node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability.js
36
36
  var require_Readability = __commonJS((exports, module) => {
37
37
  function Readability(doc, options) {
38
38
  if (options && options.documentElement) {
@@ -1603,7 +1603,7 @@ var require_Readability = __commonJS((exports, module) => {
1603
1603
  }
1604
1604
  });
1605
1605
 
1606
- // node_modules/@mozilla/readability/Readability-readerable.js
1606
+ // node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/Readability-readerable.js
1607
1607
  var require_Readability_readerable = __commonJS((exports, module) => {
1608
1608
  var REGEXPS = {
1609
1609
  unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
@@ -1659,7 +1659,7 @@ var require_Readability_readerable = __commonJS((exports, module) => {
1659
1659
  }
1660
1660
  });
1661
1661
 
1662
- // node_modules/@mozilla/readability/index.js
1662
+ // node_modules/.pnpm/@mozilla+readability@0.6.0/node_modules/@mozilla/readability/index.js
1663
1663
  var require_readability = __commonJS((exports, module) => {
1664
1664
  var Readability = require_Readability();
1665
1665
  var isProbablyReaderable = require_Readability_readerable();
@@ -1669,7 +1669,7 @@ var require_readability = __commonJS((exports, module) => {
1669
1669
  };
1670
1670
  });
1671
1671
 
1672
- // node_modules/@mixmark-io/domino/lib/Event.js
1672
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Event.js
1673
1673
  var require_Event = __commonJS((exports, module) => {
1674
1674
  module.exports = Event;
1675
1675
  Event.CAPTURING_PHASE = 1;
@@ -1726,7 +1726,7 @@ var require_Event = __commonJS((exports, module) => {
1726
1726
  });
1727
1727
  });
1728
1728
 
1729
- // node_modules/@mixmark-io/domino/lib/UIEvent.js
1729
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/UIEvent.js
1730
1730
  var require_UIEvent = __commonJS((exports, module) => {
1731
1731
  var Event = require_Event();
1732
1732
  module.exports = UIEvent;
@@ -1745,7 +1745,7 @@ var require_UIEvent = __commonJS((exports, module) => {
1745
1745
  });
1746
1746
  });
1747
1747
 
1748
- // node_modules/@mixmark-io/domino/lib/MouseEvent.js
1748
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/MouseEvent.js
1749
1749
  var require_MouseEvent = __commonJS((exports, module) => {
1750
1750
  var UIEvent = require_UIEvent();
1751
1751
  module.exports = MouseEvent;
@@ -1803,7 +1803,7 @@ var require_MouseEvent = __commonJS((exports, module) => {
1803
1803
  });
1804
1804
  });
1805
1805
 
1806
- // node_modules/@mixmark-io/domino/lib/DOMException.js
1806
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMException.js
1807
1807
  var require_DOMException = __commonJS((exports, module) => {
1808
1808
  module.exports = DOMException;
1809
1809
  var INDEX_SIZE_ERR = 1;
@@ -1927,12 +1927,12 @@ var require_DOMException = __commonJS((exports, module) => {
1927
1927
  var c;
1928
1928
  });
1929
1929
 
1930
- // node_modules/@mixmark-io/domino/lib/config.js
1930
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/config.js
1931
1931
  var require_config = __commonJS((exports) => {
1932
1932
  exports.isApiWritable = !globalThis.__domino_frozen__;
1933
1933
  });
1934
1934
 
1935
- // node_modules/@mixmark-io/domino/lib/utils.js
1935
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/utils.js
1936
1936
  var require_utils = __commonJS((exports) => {
1937
1937
  var DOMException = require_DOMException();
1938
1938
  var ERR = DOMException;
@@ -2045,7 +2045,7 @@ var require_utils = __commonJS((exports) => {
2045
2045
  };
2046
2046
  });
2047
2047
 
2048
- // node_modules/@mixmark-io/domino/lib/EventTarget.js
2048
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/EventTarget.js
2049
2049
  var require_EventTarget = __commonJS((exports, module) => {
2050
2050
  var Event = require_Event();
2051
2051
  var MouseEvent = require_MouseEvent();
@@ -2233,7 +2233,7 @@ var require_EventTarget = __commonJS((exports, module) => {
2233
2233
  };
2234
2234
  });
2235
2235
 
2236
- // node_modules/@mixmark-io/domino/lib/LinkedList.js
2236
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/LinkedList.js
2237
2237
  var require_LinkedList = __commonJS((exports, module) => {
2238
2238
  var utils = require_utils();
2239
2239
  var LinkedList = module.exports = {
@@ -2276,7 +2276,7 @@ var require_LinkedList = __commonJS((exports, module) => {
2276
2276
  };
2277
2277
  });
2278
2278
 
2279
- // node_modules/@mixmark-io/domino/lib/NodeUtils.js
2279
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeUtils.js
2280
2280
  var require_NodeUtils = __commonJS((exports, module) => {
2281
2281
  module.exports = {
2282
2282
  serializeOne,
@@ -2452,7 +2452,7 @@ var require_NodeUtils = __commonJS((exports, module) => {
2452
2452
  }
2453
2453
  });
2454
2454
 
2455
- // node_modules/@mixmark-io/domino/lib/Node.js
2455
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Node.js
2456
2456
  var require_Node = __commonJS((exports, module) => {
2457
2457
  module.exports = Node;
2458
2458
  var EventTarget = require_EventTarget();
@@ -3013,7 +3013,7 @@ var require_Node = __commonJS((exports, module) => {
3013
3013
  });
3014
3014
  });
3015
3015
 
3016
- // node_modules/@mixmark-io/domino/lib/NodeList.es6.js
3016
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.es6.js
3017
3017
  var require_NodeList_es6 = __commonJS((exports, module) => {
3018
3018
  module.exports = class NodeList extends Array {
3019
3019
  constructor(a) {
@@ -3030,7 +3030,7 @@ var require_NodeList_es6 = __commonJS((exports, module) => {
3030
3030
  };
3031
3031
  });
3032
3032
 
3033
- // node_modules/@mixmark-io/domino/lib/NodeList.es5.js
3033
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.es5.js
3034
3034
  var require_NodeList_es5 = __commonJS((exports, module) => {
3035
3035
  function item(i) {
3036
3036
  return this[i] || null;
@@ -3044,7 +3044,7 @@ var require_NodeList_es5 = __commonJS((exports, module) => {
3044
3044
  module.exports = NodeList;
3045
3045
  });
3046
3046
 
3047
- // node_modules/@mixmark-io/domino/lib/NodeList.js
3047
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeList.js
3048
3048
  var require_NodeList = __commonJS((exports, module) => {
3049
3049
  var NodeList;
3050
3050
  try {
@@ -3055,7 +3055,7 @@ var require_NodeList = __commonJS((exports, module) => {
3055
3055
  module.exports = NodeList;
3056
3056
  });
3057
3057
 
3058
- // node_modules/@mixmark-io/domino/lib/ContainerNode.js
3058
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ContainerNode.js
3059
3059
  var require_ContainerNode = __commonJS((exports, module) => {
3060
3060
  module.exports = ContainerNode;
3061
3061
  var Node = require_Node();
@@ -3123,7 +3123,7 @@ var require_ContainerNode = __commonJS((exports, module) => {
3123
3123
  });
3124
3124
  });
3125
3125
 
3126
- // node_modules/@mixmark-io/domino/lib/xmlnames.js
3126
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/xmlnames.js
3127
3127
  var require_xmlnames = __commonJS((exports) => {
3128
3128
  exports.isValidName = isValidName;
3129
3129
  exports.isValidQName = isValidQName;
@@ -3172,7 +3172,7 @@ var require_xmlnames = __commonJS((exports) => {
3172
3172
  }
3173
3173
  });
3174
3174
 
3175
- // node_modules/@mixmark-io/domino/lib/attributes.js
3175
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/attributes.js
3176
3176
  var require_attributes = __commonJS((exports) => {
3177
3177
  var utils = require_utils();
3178
3178
  exports.property = function(attr) {
@@ -3308,7 +3308,7 @@ var require_attributes = __commonJS((exports) => {
3308
3308
  };
3309
3309
  });
3310
3310
 
3311
- // node_modules/@mixmark-io/domino/lib/FilteredElementList.js
3311
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/FilteredElementList.js
3312
3312
  var require_FilteredElementList = __commonJS((exports, module) => {
3313
3313
  module.exports = FilteredElementList;
3314
3314
  var Node = require_Node();
@@ -3374,7 +3374,7 @@ var require_FilteredElementList = __commonJS((exports, module) => {
3374
3374
  });
3375
3375
  });
3376
3376
 
3377
- // node_modules/@mixmark-io/domino/lib/DOMTokenList.js
3377
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMTokenList.js
3378
3378
  var require_DOMTokenList = __commonJS((exports, module) => {
3379
3379
  var utils = require_utils();
3380
3380
  module.exports = DOMTokenList;
@@ -3536,7 +3536,7 @@ var require_DOMTokenList = __commonJS((exports, module) => {
3536
3536
  }
3537
3537
  });
3538
3538
 
3539
- // node_modules/@mixmark-io/domino/lib/select.js
3539
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/select.js
3540
3540
  var require_select = __commonJS((exports, module) => {
3541
3541
  var window2 = Object.create(null, {
3542
3542
  location: { get: function() {
@@ -4279,7 +4279,7 @@ var require_select = __commonJS((exports, module) => {
4279
4279
  };
4280
4280
  });
4281
4281
 
4282
- // node_modules/@mixmark-io/domino/lib/ChildNode.js
4282
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ChildNode.js
4283
4283
  var require_ChildNode = __commonJS((exports, module) => {
4284
4284
  var Node = require_Node();
4285
4285
  var LinkedList = require_LinkedList();
@@ -4369,7 +4369,7 @@ var require_ChildNode = __commonJS((exports, module) => {
4369
4369
  module.exports = ChildNode;
4370
4370
  });
4371
4371
 
4372
- // node_modules/@mixmark-io/domino/lib/NonDocumentTypeChildNode.js
4372
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NonDocumentTypeChildNode.js
4373
4373
  var require_NonDocumentTypeChildNode = __commonJS((exports, module) => {
4374
4374
  var Node = require_Node();
4375
4375
  var NonDocumentTypeChildNode = {
@@ -4395,7 +4395,7 @@ var require_NonDocumentTypeChildNode = __commonJS((exports, module) => {
4395
4395
  module.exports = NonDocumentTypeChildNode;
4396
4396
  });
4397
4397
 
4398
- // node_modules/@mixmark-io/domino/lib/NamedNodeMap.js
4398
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NamedNodeMap.js
4399
4399
  var require_NamedNodeMap = __commonJS((exports, module) => {
4400
4400
  module.exports = NamedNodeMap;
4401
4401
  var utils = require_utils();
@@ -4432,7 +4432,7 @@ var require_NamedNodeMap = __commonJS((exports, module) => {
4432
4432
  });
4433
4433
  });
4434
4434
 
4435
- // node_modules/@mixmark-io/domino/lib/Element.js
4435
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Element.js
4436
4436
  var require_Element = __commonJS((exports, module) => {
4437
4437
  module.exports = Element;
4438
4438
  var xml = require_xmlnames();
@@ -5333,7 +5333,7 @@ var require_Element = __commonJS((exports, module) => {
5333
5333
  }
5334
5334
  });
5335
5335
 
5336
- // node_modules/@mixmark-io/domino/lib/Leaf.js
5336
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Leaf.js
5337
5337
  var require_Leaf = __commonJS((exports, module) => {
5338
5338
  module.exports = Leaf;
5339
5339
  var Node = require_Node();
@@ -5374,7 +5374,7 @@ var require_Leaf = __commonJS((exports, module) => {
5374
5374
  });
5375
5375
  });
5376
5376
 
5377
- // node_modules/@mixmark-io/domino/lib/CharacterData.js
5377
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CharacterData.js
5378
5378
  var require_CharacterData = __commonJS((exports, module) => {
5379
5379
  module.exports = CharacterData;
5380
5380
  var Leaf = require_Leaf();
@@ -5431,7 +5431,7 @@ var require_CharacterData = __commonJS((exports, module) => {
5431
5431
  Object.defineProperties(CharacterData.prototype, NonDocumentTypeChildNode);
5432
5432
  });
5433
5433
 
5434
- // node_modules/@mixmark-io/domino/lib/Text.js
5434
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Text.js
5435
5435
  var require_Text = __commonJS((exports, module) => {
5436
5436
  module.exports = Text;
5437
5437
  var utils = require_utils();
@@ -5501,7 +5501,7 @@ var require_Text = __commonJS((exports, module) => {
5501
5501
  });
5502
5502
  });
5503
5503
 
5504
- // node_modules/@mixmark-io/domino/lib/Comment.js
5504
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Comment.js
5505
5505
  var require_Comment = __commonJS((exports, module) => {
5506
5506
  module.exports = Comment;
5507
5507
  var Node = require_Node();
@@ -5544,7 +5544,7 @@ var require_Comment = __commonJS((exports, module) => {
5544
5544
  });
5545
5545
  });
5546
5546
 
5547
- // node_modules/@mixmark-io/domino/lib/DocumentFragment.js
5547
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DocumentFragment.js
5548
5548
  var require_DocumentFragment = __commonJS((exports, module) => {
5549
5549
  module.exports = DocumentFragment;
5550
5550
  var Node = require_Node();
@@ -5601,7 +5601,7 @@ var require_DocumentFragment = __commonJS((exports, module) => {
5601
5601
  });
5602
5602
  });
5603
5603
 
5604
- // node_modules/@mixmark-io/domino/lib/ProcessingInstruction.js
5604
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/ProcessingInstruction.js
5605
5605
  var require_ProcessingInstruction = __commonJS((exports, module) => {
5606
5606
  module.exports = ProcessingInstruction;
5607
5607
  var Node = require_Node();
@@ -5650,7 +5650,7 @@ var require_ProcessingInstruction = __commonJS((exports, module) => {
5650
5650
  });
5651
5651
  });
5652
5652
 
5653
- // node_modules/@mixmark-io/domino/lib/NodeFilter.js
5653
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeFilter.js
5654
5654
  var require_NodeFilter = __commonJS((exports, module) => {
5655
5655
  var NodeFilter = {
5656
5656
  FILTER_ACCEPT: 1,
@@ -5673,7 +5673,7 @@ var require_NodeFilter = __commonJS((exports, module) => {
5673
5673
  module.exports = NodeFilter.constructor = NodeFilter.prototype = NodeFilter;
5674
5674
  });
5675
5675
 
5676
- // node_modules/@mixmark-io/domino/lib/NodeTraversal.js
5676
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeTraversal.js
5677
5677
  var require_NodeTraversal = __commonJS((exports, module) => {
5678
5678
  var NodeTraversal = module.exports = {
5679
5679
  nextSkippingChildren,
@@ -5737,7 +5737,7 @@ var require_NodeTraversal = __commonJS((exports, module) => {
5737
5737
  }
5738
5738
  });
5739
5739
 
5740
- // node_modules/@mixmark-io/domino/lib/TreeWalker.js
5740
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/TreeWalker.js
5741
5741
  var require_TreeWalker = __commonJS((exports, module) => {
5742
5742
  module.exports = TreeWalker;
5743
5743
  var Node = require_Node();
@@ -5967,7 +5967,7 @@ var require_TreeWalker = __commonJS((exports, module) => {
5967
5967
  });
5968
5968
  });
5969
5969
 
5970
- // node_modules/@mixmark-io/domino/lib/NodeIterator.js
5970
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NodeIterator.js
5971
5971
  var require_NodeIterator = __commonJS((exports, module) => {
5972
5972
  module.exports = NodeIterator;
5973
5973
  var NodeFilter = require_NodeFilter();
@@ -6108,7 +6108,7 @@ var require_NodeIterator = __commonJS((exports, module) => {
6108
6108
  });
6109
6109
  });
6110
6110
 
6111
- // node_modules/@mixmark-io/domino/lib/URL.js
6111
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/URL.js
6112
6112
  var require_URL = __commonJS((exports, module) => {
6113
6113
  module.exports = URL4;
6114
6114
  function URL4(url) {
@@ -6281,7 +6281,7 @@ var require_URL = __commonJS((exports, module) => {
6281
6281
  };
6282
6282
  });
6283
6283
 
6284
- // node_modules/@mixmark-io/domino/lib/CustomEvent.js
6284
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CustomEvent.js
6285
6285
  var require_CustomEvent = __commonJS((exports, module) => {
6286
6286
  module.exports = CustomEvent;
6287
6287
  var Event = require_Event();
@@ -6293,7 +6293,7 @@ var require_CustomEvent = __commonJS((exports, module) => {
6293
6293
  });
6294
6294
  });
6295
6295
 
6296
- // node_modules/@mixmark-io/domino/lib/events.js
6296
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/events.js
6297
6297
  var require_events = __commonJS((exports, module) => {
6298
6298
  module.exports = {
6299
6299
  Event: require_Event(),
@@ -6303,7 +6303,7 @@ var require_events = __commonJS((exports, module) => {
6303
6303
  };
6304
6304
  });
6305
6305
 
6306
- // node_modules/@mixmark-io/domino/lib/style_parser.js
6306
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/style_parser.js
6307
6307
  var require_style_parser = __commonJS((exports) => {
6308
6308
  Object.defineProperty(exports, "__esModule", { value: true });
6309
6309
  exports.hyphenate = exports.parse = undefined;
@@ -6370,7 +6370,7 @@ var require_style_parser = __commonJS((exports) => {
6370
6370
  exports.hyphenate = hyphenate;
6371
6371
  });
6372
6372
 
6373
- // node_modules/@mixmark-io/domino/lib/CSSStyleDeclaration.js
6373
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/CSSStyleDeclaration.js
6374
6374
  var require_CSSStyleDeclaration = __commonJS((exports, module) => {
6375
6375
  var { parse } = require_style_parser();
6376
6376
  module.exports = function(elt) {
@@ -6546,7 +6546,7 @@ var require_CSSStyleDeclaration = __commonJS((exports, module) => {
6546
6546
  });
6547
6547
  });
6548
6548
 
6549
- // node_modules/@mixmark-io/domino/lib/URLUtils.js
6549
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/URLUtils.js
6550
6550
  var require_URLUtils = __commonJS((exports, module) => {
6551
6551
  var URL4 = require_URL();
6552
6552
  module.exports = URLUtils;
@@ -6780,7 +6780,7 @@ var require_URLUtils = __commonJS((exports, module) => {
6780
6780
  };
6781
6781
  });
6782
6782
 
6783
- // node_modules/@mixmark-io/domino/lib/defineElement.js
6783
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/defineElement.js
6784
6784
  var require_defineElement = __commonJS((exports, module) => {
6785
6785
  var attributes = require_attributes();
6786
6786
  var isApiWritable = require_config().isApiWritable;
@@ -6842,7 +6842,7 @@ var require_defineElement = __commonJS((exports, module) => {
6842
6842
  }
6843
6843
  });
6844
6844
 
6845
- // node_modules/@mixmark-io/domino/lib/htmlelts.js
6845
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/htmlelts.js
6846
6846
  var require_htmlelts = __commonJS((exports) => {
6847
6847
  var Node = require_Node();
6848
6848
  var Element = require_Element();
@@ -8306,7 +8306,7 @@ var require_htmlelts = __commonJS((exports) => {
8306
8306
  });
8307
8307
  });
8308
8308
 
8309
- // node_modules/@mixmark-io/domino/lib/svg.js
8309
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/svg.js
8310
8310
  var require_svg = __commonJS((exports) => {
8311
8311
  var Element = require_Element();
8312
8312
  var defineElement = require_defineElement();
@@ -8432,7 +8432,7 @@ var require_svg = __commonJS((exports) => {
8432
8432
  });
8433
8433
  });
8434
8434
 
8435
- // node_modules/@mixmark-io/domino/lib/MutationConstants.js
8435
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/MutationConstants.js
8436
8436
  var require_MutationConstants = __commonJS((exports, module) => {
8437
8437
  module.exports = {
8438
8438
  VALUE: 1,
@@ -8444,7 +8444,7 @@ var require_MutationConstants = __commonJS((exports, module) => {
8444
8444
  };
8445
8445
  });
8446
8446
 
8447
- // node_modules/@mixmark-io/domino/lib/Document.js
8447
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Document.js
8448
8448
  var require_Document = __commonJS((exports, module) => {
8449
8449
  module.exports = Document;
8450
8450
  var Node = require_Node();
@@ -9156,7 +9156,7 @@ var require_Document = __commonJS((exports, module) => {
9156
9156
  };
9157
9157
  });
9158
9158
 
9159
- // node_modules/@mixmark-io/domino/lib/DocumentType.js
9159
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DocumentType.js
9160
9160
  var require_DocumentType = __commonJS((exports, module) => {
9161
9161
  module.exports = DocumentType;
9162
9162
  var Node = require_Node();
@@ -9190,7 +9190,7 @@ var require_DocumentType = __commonJS((exports, module) => {
9190
9190
  Object.defineProperties(DocumentType.prototype, ChildNode);
9191
9191
  });
9192
9192
 
9193
- // node_modules/@mixmark-io/domino/lib/HTMLParser.js
9193
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/HTMLParser.js
9194
9194
  var require_HTMLParser = __commonJS((exports, module) => {
9195
9195
  module.exports = HTMLParser;
9196
9196
  var Document = require_Document();
@@ -17241,7 +17241,7 @@ var require_HTMLParser = __commonJS((exports, module) => {
17241
17241
  }
17242
17242
  });
17243
17243
 
17244
- // node_modules/@mixmark-io/domino/lib/DOMImplementation.js
17244
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/DOMImplementation.js
17245
17245
  var require_DOMImplementation = __commonJS((exports, module) => {
17246
17246
  module.exports = DOMImplementation;
17247
17247
  var Document = require_Document();
@@ -17315,7 +17315,7 @@ var require_DOMImplementation = __commonJS((exports, module) => {
17315
17315
  };
17316
17316
  });
17317
17317
 
17318
- // node_modules/@mixmark-io/domino/lib/Location.js
17318
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Location.js
17319
17319
  var require_Location = __commonJS((exports, module) => {
17320
17320
  var URL4 = require_URL();
17321
17321
  var URLUtils = require_URLUtils();
@@ -17351,7 +17351,7 @@ var require_Location = __commonJS((exports, module) => {
17351
17351
  });
17352
17352
  });
17353
17353
 
17354
- // node_modules/@mixmark-io/domino/lib/NavigatorID.js
17354
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/NavigatorID.js
17355
17355
  var require_NavigatorID = __commonJS((exports, module) => {
17356
17356
  var NavigatorID = Object.create(null, {
17357
17357
  appCodeName: { value: "Mozilla" },
@@ -17370,7 +17370,7 @@ var require_NavigatorID = __commonJS((exports, module) => {
17370
17370
  module.exports = NavigatorID;
17371
17371
  });
17372
17372
 
17373
- // node_modules/@mixmark-io/domino/lib/WindowTimers.js
17373
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/WindowTimers.js
17374
17374
  var require_WindowTimers = __commonJS((exports, module) => {
17375
17375
  var WindowTimers = {
17376
17376
  setTimeout,
@@ -17381,7 +17381,7 @@ var require_WindowTimers = __commonJS((exports, module) => {
17381
17381
  module.exports = WindowTimers;
17382
17382
  });
17383
17383
 
17384
- // node_modules/@mixmark-io/domino/lib/impl.js
17384
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/impl.js
17385
17385
  var require_impl = __commonJS((exports, module) => {
17386
17386
  var utils = require_utils();
17387
17387
  exports = module.exports = {
@@ -17409,7 +17409,7 @@ var require_impl = __commonJS((exports, module) => {
17409
17409
  utils.merge(exports, require_svg().elements);
17410
17410
  });
17411
17411
 
17412
- // node_modules/@mixmark-io/domino/lib/Window.js
17412
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/Window.js
17413
17413
  var require_Window = __commonJS((exports, module) => {
17414
17414
  var DOMImplementation = require_DOMImplementation();
17415
17415
  var EventTarget = require_EventTarget();
@@ -17464,7 +17464,7 @@ var require_Window = __commonJS((exports, module) => {
17464
17464
  utils.expose(require_impl(), Window);
17465
17465
  });
17466
17466
 
17467
- // node_modules/@mixmark-io/domino/lib/index.js
17467
+ // node_modules/.pnpm/@mixmark-io+domino@2.2.0/node_modules/@mixmark-io/domino/lib/index.js
17468
17468
  var require_lib = __commonJS((exports) => {
17469
17469
  var DOMImplementation = require_DOMImplementation();
17470
17470
  var HTMLParser = require_HTMLParser();
@@ -17514,7 +17514,7 @@ var require_lib = __commonJS((exports) => {
17514
17514
  exports.impl = impl;
17515
17515
  });
17516
17516
 
17517
- // node_modules/turndown/lib/turndown.cjs.js
17517
+ // node_modules/.pnpm/turndown@7.2.4/node_modules/turndown/lib/turndown.cjs.js
17518
17518
  var require_turndown_cjs = __commonJS((exports, module) => {
17519
17519
  function extend(destination) {
17520
17520
  for (var i = 1;i < arguments.length; i++) {
@@ -18220,6 +18220,12 @@ var CUSTOM_SKILLS = [
18220
18220
  description: "Repository understanding and hierarchical codemap generation",
18221
18221
  allowedAgents: ["orchestrator"],
18222
18222
  sourcePath: "src/skills/codemap"
18223
+ },
18224
+ {
18225
+ name: "clonedeps",
18226
+ description: "Clone important dependency source for local inspection",
18227
+ allowedAgents: ["orchestrator"],
18228
+ sourcePath: "src/skills/clonedeps"
18223
18229
  }
18224
18230
  ];
18225
18231
 
@@ -18393,6 +18399,56 @@ var CouncilConfigSchema = z.object({
18393
18399
  import * as fs from "node:fs";
18394
18400
  import * as path from "node:path";
18395
18401
 
18402
+ // src/utils/compat.ts
18403
+ import { spawn as nodeSpawn } from "node:child_process";
18404
+ import { writeFile as fsWriteFile } from "node:fs/promises";
18405
+ var isBun = typeof globalThis.Bun !== "undefined";
18406
+ function collectStream(stream) {
18407
+ if (!stream)
18408
+ return () => Promise.resolve("");
18409
+ const chunks = [];
18410
+ stream.on("data", (chunk) => chunks.push(chunk));
18411
+ return () => new Promise((resolve, reject) => {
18412
+ if (!stream.readable) {
18413
+ resolve(Buffer.concat(chunks).toString("utf-8"));
18414
+ return;
18415
+ }
18416
+ stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
18417
+ stream.on("error", reject);
18418
+ });
18419
+ }
18420
+ function crossSpawn(command, options) {
18421
+ const [cmd, ...args] = command;
18422
+ const proc = nodeSpawn(cmd, args, {
18423
+ stdio: [
18424
+ options?.stdin ?? "ignore",
18425
+ options?.stdout ?? "pipe",
18426
+ options?.stderr ?? "pipe"
18427
+ ],
18428
+ cwd: options?.cwd,
18429
+ env: options?.env
18430
+ });
18431
+ const stdoutCollector = collectStream(proc.stdout);
18432
+ const stderrCollector = collectStream(proc.stderr);
18433
+ const exited = new Promise((resolve, reject) => {
18434
+ proc.on("error", reject);
18435
+ proc.on("close", (code) => resolve(code ?? 1));
18436
+ });
18437
+ return {
18438
+ proc,
18439
+ stdout: stdoutCollector,
18440
+ stderr: stderrCollector,
18441
+ exited,
18442
+ kill: (signal) => proc.kill(signal),
18443
+ get exitCode() {
18444
+ return proc.exitCode;
18445
+ }
18446
+ };
18447
+ }
18448
+ async function crossWrite(path, data) {
18449
+ await fsWriteFile(path, Buffer.from(data));
18450
+ }
18451
+
18396
18452
  // src/config/agent-mcps.ts
18397
18453
  var DEFAULT_AGENT_MCPS = {
18398
18454
  orchestrator: ["*", "!context7"],
@@ -18842,13 +18898,16 @@ function parseModelReference(model) {
18842
18898
  modelID: model.slice(slashIndex + 1)
18843
18899
  };
18844
18900
  }
18845
- async function promptWithTimeout(client, args, timeoutMs) {
18901
+ async function promptWithTimeout(client, args, timeoutMs, signal) {
18902
+ if (signal?.aborted)
18903
+ throw new Error("Prompt cancelled");
18846
18904
  if (timeoutMs <= 0) {
18847
18905
  await client.session.prompt(args);
18848
18906
  return;
18849
18907
  }
18850
18908
  const sessionId = args.path.id;
18851
18909
  let timer;
18910
+ let onAbort;
18852
18911
  try {
18853
18912
  const promptPromise = client.session.prompt(args);
18854
18913
  promptPromise.catch(() => {});
@@ -18858,6 +18917,16 @@ async function promptWithTimeout(client, args, timeoutMs) {
18858
18917
  timer = setTimeout(() => {
18859
18918
  reject(new OperationTimeoutError(`Prompt timed out after ${timeoutMs}ms`));
18860
18919
  }, timeoutMs);
18920
+ }),
18921
+ new Promise((_, reject) => {
18922
+ if (!signal)
18923
+ return;
18924
+ if (signal.aborted) {
18925
+ reject(new Error("Prompt cancelled"));
18926
+ return;
18927
+ }
18928
+ onAbort = () => reject(new Error("Prompt cancelled"));
18929
+ signal.addEventListener("abort", onAbort, { once: true });
18861
18930
  })
18862
18931
  ]);
18863
18932
  } catch (error) {
@@ -18869,12 +18938,15 @@ async function promptWithTimeout(client, args, timeoutMs) {
18869
18938
  throw error;
18870
18939
  } finally {
18871
18940
  clearTimeout(timer);
18941
+ if (onAbort)
18942
+ signal?.removeEventListener("abort", onAbort);
18872
18943
  }
18873
18944
  }
18874
18945
  async function extractSessionResult(client, sessionId, options) {
18875
18946
  const includeReasoning = options?.includeReasoning ?? true;
18876
18947
  const messagesResult = await client.session.messages({
18877
- path: { id: sessionId }
18948
+ path: { id: sessionId },
18949
+ ...options?.directory ? { query: { directory: options.directory } } : {}
18878
18950
  });
18879
18951
  const messages = messagesResult.data ?? [];
18880
18952
  const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
@@ -19030,6 +19102,25 @@ ${enabledParallelExamples}
19030
19102
 
19031
19103
  Balance: respect dependencies, avoid parallelizing what must be sequential.
19032
19104
 
19105
+ ### Context Isolation
19106
+ If no specialist delegation is needed, consider \`subtask\` before doing
19107
+ context-heavy work directly.
19108
+
19109
+ Ask whether the parent context needs the details or only the result. Use
19110
+ \`subtask\` when the work is bounded, context-heavy, and the parent only needs a
19111
+ compact outcome.
19112
+
19113
+ Use \`subtask\` for focused investigation, bounded analysis, cleanup, or
19114
+ verification across files/logs/messages.
19115
+
19116
+ Do not use \`subtask\` for tiny tasks, open-ended work, interactive decisions,
19117
+ work better handled by a named specialist, or cases where the parent must reason
19118
+ over the details.
19119
+
19120
+ When calling \`subtask\`, give a self-contained prompt with objective,
19121
+ constraints, relevant context, deliverable, and validation. Pass only clearly
19122
+ relevant files. Wait for the summary, then integrate and verify it.
19123
+
19033
19124
  ### OpenCode subagent execution model
19034
19125
  - A delegated specialist runs in a separate child session.
19035
19126
  - Delegation is blocking for the parent at that point: send work out, then continue that line after results return.
@@ -21966,6 +22057,15 @@ var APPLY_PATCH_RESCUE_OPTIONS = {
21966
22057
  prefixSuffix: true,
21967
22058
  lcsRescue: true
21968
22059
  };
22060
+ function replacePatchArgs(output, args, patchText) {
22061
+ const nextArgs = { ...args, patchText };
22062
+ try {
22063
+ output.args = nextArgs;
22064
+ } catch {
22065
+ return false;
22066
+ }
22067
+ return output.args?.patchText === patchText;
22068
+ }
21969
22069
  function createApplyPatchHook(ctx) {
21970
22070
  function logHookStatus(state, data) {
21971
22071
  log(`apply-patch hook ${state}`, data);
@@ -21985,8 +22085,16 @@ function createApplyPatchHook(ctx) {
21985
22085
  try {
21986
22086
  const result = await rewritePatch(root, patchText, APPLY_PATCH_RESCUE_OPTIONS, worktree);
21987
22087
  if (result.changed) {
21988
- args.patchText = result.patchText;
21989
- logHookStatus("rewrite");
22088
+ if (replacePatchArgs(output, args, result.patchText)) {
22089
+ logHookStatus("rewrite");
22090
+ } else {
22091
+ logHookStatus("skipped", {
22092
+ reason: "readonly output args",
22093
+ failOpen: true,
22094
+ rescueOptions: APPLY_PATCH_RESCUE_OPTIONS,
22095
+ rewriteStage: "before-native"
22096
+ });
22097
+ }
21990
22098
  return;
21991
22099
  }
21992
22100
  logHookStatus("unchanged");
@@ -22018,56 +22126,6 @@ function createApplyPatchHook(ctx) {
22018
22126
  }
22019
22127
  };
22020
22128
  }
22021
- // src/utils/compat.ts
22022
- import { spawn as nodeSpawn } from "node:child_process";
22023
- import { writeFile as fsWriteFile } from "node:fs/promises";
22024
- var isBun = typeof globalThis.Bun !== "undefined";
22025
- function collectStream(stream) {
22026
- if (!stream)
22027
- return () => Promise.resolve("");
22028
- const chunks = [];
22029
- stream.on("data", (chunk) => chunks.push(chunk));
22030
- return () => new Promise((resolve, reject) => {
22031
- if (!stream.readable) {
22032
- resolve(Buffer.concat(chunks).toString("utf-8"));
22033
- return;
22034
- }
22035
- stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
22036
- stream.on("error", reject);
22037
- });
22038
- }
22039
- function crossSpawn(command, options) {
22040
- const [cmd, ...args] = command;
22041
- const proc = nodeSpawn(cmd, args, {
22042
- stdio: [
22043
- options?.stdin ?? "ignore",
22044
- options?.stdout ?? "pipe",
22045
- options?.stderr ?? "pipe"
22046
- ],
22047
- cwd: options?.cwd,
22048
- env: options?.env
22049
- });
22050
- const stdoutCollector = collectStream(proc.stdout);
22051
- const stderrCollector = collectStream(proc.stderr);
22052
- const exited = new Promise((resolve, reject) => {
22053
- proc.on("error", reject);
22054
- proc.on("close", (code) => resolve(code ?? 1));
22055
- });
22056
- return {
22057
- proc,
22058
- stdout: stdoutCollector,
22059
- stderr: stderrCollector,
22060
- exited,
22061
- kill: (signal) => proc.kill(signal),
22062
- get exitCode() {
22063
- return proc.exitCode;
22064
- }
22065
- };
22066
- }
22067
- async function crossWrite(path6, data) {
22068
- await fsWriteFile(path6, Buffer.from(data));
22069
- }
22070
-
22071
22129
  // src/hooks/auto-update-checker/cache.ts
22072
22130
  import * as fs5 from "node:fs";
22073
22131
  import * as path8 from "node:path";
@@ -23662,8 +23720,338 @@ function createPostFileToolNudgeHook(options = {}) {
23662
23720
  }
23663
23721
  };
23664
23722
  }
23723
+ // src/hooks/session-goal/index.ts
23724
+ import * as fs7 from "node:fs/promises";
23725
+
23726
+ // src/interview/document.ts
23727
+ import * as fsSync from "node:fs";
23728
+ import * as fs6 from "node:fs/promises";
23729
+ import * as path9 from "node:path";
23730
+ var DEFAULT_OUTPUT_FOLDER = "interview";
23731
+ function normalizeOutputFolder(outputFolder) {
23732
+ const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
23733
+ return normalized || DEFAULT_OUTPUT_FOLDER;
23734
+ }
23735
+ function createInterviewDirectoryPath(directory, outputFolder) {
23736
+ return path9.join(directory, normalizeOutputFolder(outputFolder));
23737
+ }
23738
+ function createInterviewFilePath(directory, outputFolder, idea) {
23739
+ const fileName = `${slugify(idea) || "interview"}.md`;
23740
+ return path9.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
23741
+ }
23742
+ function relativeInterviewPath(directory, filePath) {
23743
+ return path9.relative(directory, filePath) || path9.basename(filePath);
23744
+ }
23745
+ function resolveExistingInterviewPath(directory, outputFolder, value) {
23746
+ const trimmed = value.trim();
23747
+ if (!trimmed) {
23748
+ return null;
23749
+ }
23750
+ const outputDir = createInterviewDirectoryPath(directory, outputFolder);
23751
+ const candidates = new Set;
23752
+ const resolvedRoot = path9.resolve(directory);
23753
+ if (path9.isAbsolute(trimmed)) {
23754
+ candidates.add(trimmed);
23755
+ } else {
23756
+ candidates.add(path9.resolve(directory, trimmed));
23757
+ candidates.add(path9.join(outputDir, trimmed));
23758
+ if (!trimmed.endsWith(".md")) {
23759
+ candidates.add(path9.join(outputDir, `${trimmed}.md`));
23760
+ }
23761
+ }
23762
+ for (const candidate of candidates) {
23763
+ if (path9.extname(candidate) !== ".md") {
23764
+ continue;
23765
+ }
23766
+ const resolved = path9.resolve(candidate);
23767
+ if (!resolved.startsWith(resolvedRoot + path9.sep) && resolved !== resolvedRoot) {
23768
+ continue;
23769
+ }
23770
+ if (fsSync.existsSync(candidate)) {
23771
+ return candidate;
23772
+ }
23773
+ }
23774
+ return null;
23775
+ }
23776
+ function slugify(value) {
23777
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
23778
+ }
23779
+ function extractHistorySection(document) {
23780
+ const marker = `## Q&A history
23781
+
23782
+ `;
23783
+ const index = document.indexOf(marker);
23784
+ return index >= 0 ? document.slice(index + marker.length).trim() : "";
23785
+ }
23786
+ function extractSummarySection(document) {
23787
+ const marker = `## Current spec
23788
+
23789
+ `;
23790
+ const historyMarker = `
23791
+
23792
+ ## Q&A history`;
23793
+ const start = document.indexOf(marker);
23794
+ if (start < 0) {
23795
+ return "";
23796
+ }
23797
+ const summaryStart = start + marker.length;
23798
+ const summaryEnd = document.indexOf(historyMarker, summaryStart);
23799
+ return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
23800
+ }
23801
+ function extractTitle(document) {
23802
+ const match = document.match(/^#\s+(.+)$/m);
23803
+ return match?.[1]?.trim() ?? "";
23804
+ }
23805
+ function buildInterviewDocument(idea, summary, history, meta) {
23806
+ const normalizedSummary = summary.trim() || "Waiting for interview answers.";
23807
+ const normalizedHistory = history.trim() || "No answers yet.";
23808
+ const frontmatter = meta?.sessionID ? [
23809
+ "---",
23810
+ `sessionID: ${meta.sessionID}`,
23811
+ `baseMessageCount: ${meta.baseMessageCount ?? 0}`,
23812
+ `updatedAt: ${new Date().toISOString()}`,
23813
+ "---",
23814
+ ""
23815
+ ].join(`
23816
+ `) : "";
23817
+ return [
23818
+ frontmatter,
23819
+ `# ${idea}`,
23820
+ "",
23821
+ "## Current spec",
23822
+ "",
23823
+ normalizedSummary,
23824
+ "",
23825
+ "## Q&A history",
23826
+ "",
23827
+ normalizedHistory,
23828
+ ""
23829
+ ].join(`
23830
+ `);
23831
+ }
23832
+ function parseFrontmatter(content) {
23833
+ const match = content.match(/^---\n([\s\S]*?)\n---\n/);
23834
+ if (!match)
23835
+ return null;
23836
+ const result = {};
23837
+ for (const line of match[1].split(`
23838
+ `)) {
23839
+ const colonIdx = line.indexOf(":");
23840
+ if (colonIdx > 0) {
23841
+ result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
23842
+ }
23843
+ }
23844
+ return result;
23845
+ }
23846
+ async function ensureInterviewFile(record) {
23847
+ await fs6.mkdir(path9.dirname(record.markdownPath), { recursive: true });
23848
+ try {
23849
+ await fs6.access(record.markdownPath);
23850
+ } catch {
23851
+ await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
23852
+ sessionID: record.sessionID,
23853
+ baseMessageCount: record.baseMessageCount
23854
+ }), "utf8");
23855
+ }
23856
+ }
23857
+ async function readInterviewDocument(record) {
23858
+ try {
23859
+ return await fs6.readFile(record.markdownPath, "utf8");
23860
+ } catch {}
23861
+ await ensureInterviewFile(record);
23862
+ return fs6.readFile(record.markdownPath, "utf8");
23863
+ }
23864
+ async function rewriteInterviewDocument(record, summary) {
23865
+ const existing = await readInterviewDocument(record);
23866
+ const history = extractHistorySection(existing);
23867
+ const next = buildInterviewDocument(record.idea, summary, history, {
23868
+ sessionID: record.sessionID,
23869
+ baseMessageCount: record.baseMessageCount
23870
+ });
23871
+ await fs6.writeFile(record.markdownPath, next, "utf8");
23872
+ return next;
23873
+ }
23874
+ async function appendInterviewAnswers(record, questions, answers) {
23875
+ const existing = await readInterviewDocument(record);
23876
+ const summary = extractSummarySection(existing);
23877
+ const history = extractHistorySection(existing);
23878
+ const questionMap = new Map(questions.map((question) => [question.id, question]));
23879
+ const appended = answers.map((answer) => {
23880
+ const question = questionMap.get(answer.questionId);
23881
+ return question ? `Q: ${question.question}
23882
+ A: ${answer.answer.trim()}` : null;
23883
+ }).filter((value) => value !== null).join(`
23884
+
23885
+ `);
23886
+ const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
23887
+
23888
+ `);
23889
+ await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
23890
+ sessionID: record.sessionID,
23891
+ baseMessageCount: record.baseMessageCount
23892
+ }), "utf8");
23893
+ }
23894
+
23895
+ // src/hooks/session-goal/index.ts
23896
+ var COMMAND_NAME = "goal";
23897
+ var MAX_GOAL_LENGTH = 4000;
23898
+ function normalizeGoalText(text) {
23899
+ return text.trim().replace(/\s+/g, " ").slice(0, MAX_GOAL_LENGTH);
23900
+ }
23901
+ function trimGoalText(text) {
23902
+ return text.trim().slice(0, MAX_GOAL_LENGTH);
23903
+ }
23904
+ function pushText(output, text) {
23905
+ output.parts.push(createInternalAgentTextPart(text));
23906
+ }
23907
+ function formatGoal(state, inherited) {
23908
+ const tag = inherited ? "parent_goal" : "active_goal";
23909
+ const guidance = inherited ? "This is context only. Your delegated prompt remains the bounded task." : "Use todos as the execution ledger. Keep planning, delegation, edits, and verification aligned to this goal. Do not broaden scope unless the user changes the goal.";
23910
+ return `<${tag}>
23911
+ Objective: ${state.text}
23912
+ ${guidance}
23913
+ </${tag}>`;
23914
+ }
23915
+ async function readInterviewGoal(directory, outputFolder, value) {
23916
+ try {
23917
+ const sourcePath = resolveExistingInterviewPath(directory, outputFolder, value);
23918
+ if (!sourcePath)
23919
+ return null;
23920
+ const content = await fs7.readFile(sourcePath, "utf8");
23921
+ const title = extractTitle(content);
23922
+ const summary = extractSummarySection(content);
23923
+ const text = trimGoalText([title ? `From interview: ${title}` : "", summary].filter(Boolean).join(`
23924
+
23925
+ `));
23926
+ return text ? { text, sourcePath } : null;
23927
+ } catch {
23928
+ return null;
23929
+ }
23930
+ }
23931
+ function resolveGoal(goals, sessionID) {
23932
+ const seen = new Set;
23933
+ let currentSessionID = sessionID;
23934
+ let inherited = false;
23935
+ while (true) {
23936
+ if (seen.has(currentSessionID)) {
23937
+ goals.delete(sessionID);
23938
+ return null;
23939
+ }
23940
+ seen.add(currentSessionID);
23941
+ const goal = goals.get(currentSessionID);
23942
+ if (!goal) {
23943
+ goals.delete(sessionID);
23944
+ return null;
23945
+ }
23946
+ if (!goal.inheritedFrom) {
23947
+ return { goal, inherited };
23948
+ }
23949
+ inherited = true;
23950
+ currentSessionID = goal.inheritedFrom;
23951
+ }
23952
+ }
23953
+ function createSessionGoalHook(ctx, config, options) {
23954
+ const goals = new Map;
23955
+ const outputFolder = config.interview?.outputFolder ?? "interview";
23956
+ return {
23957
+ registerCommand: (opencodeConfig) => {
23958
+ const commandConfig = opencodeConfig.command;
23959
+ if (commandConfig?.[COMMAND_NAME])
23960
+ return;
23961
+ if (!opencodeConfig.command)
23962
+ opencodeConfig.command = {};
23963
+ opencodeConfig.command[COMMAND_NAME] = {
23964
+ template: "Set or show the current session goal",
23965
+ description: "Pin a session objective that keeps todos, delegation, and verification aligned"
23966
+ };
23967
+ },
23968
+ handleCommandExecuteBefore: async (input, output) => {
23969
+ if (input.command !== COMMAND_NAME)
23970
+ return;
23971
+ output.parts.length = 0;
23972
+ const args = input.arguments.trim();
23973
+ if (!args) {
23974
+ const resolved = resolveGoal(goals, input.sessionID);
23975
+ pushText(output, resolved ? `Active goal:
23976
+ ${resolved.goal.text}
23977
+
23978
+ Use todos for execution steps. Auto-continuation continues only while todos remain.` : "No active goal. Set one with /goal <objective>.");
23979
+ return;
23980
+ }
23981
+ if (args === "clear") {
23982
+ goals.delete(input.sessionID);
23983
+ pushText(output, "Cleared the active goal for this session.");
23984
+ return;
23985
+ }
23986
+ if (args.startsWith("from ")) {
23987
+ const value = args.slice("from ".length).trim();
23988
+ const interviewGoal = await readInterviewGoal(ctx.directory, outputFolder, value);
23989
+ if (!interviewGoal) {
23990
+ pushText(output, `Could not find a readable interview spec for "${value}".`);
23991
+ return;
23992
+ }
23993
+ goals.set(input.sessionID, {
23994
+ text: interviewGoal.text,
23995
+ source: "interview",
23996
+ sourcePath: interviewGoal.sourcePath,
23997
+ createdAt: Date.now()
23998
+ });
23999
+ pushText(output, `Set active goal from interview:
24000
+ ${interviewGoal.text}`);
24001
+ return;
24002
+ }
24003
+ const text = normalizeGoalText(args);
24004
+ goals.set(input.sessionID, {
24005
+ text,
24006
+ source: "manual",
24007
+ createdAt: Date.now()
24008
+ });
24009
+ pushText(output, `Set active goal:
24010
+ ${text}`);
24011
+ },
24012
+ handleEvent: (input) => {
24013
+ const event = input.event;
24014
+ if (event.type === "session.created") {
24015
+ const info = event.properties?.info;
24016
+ if (!info?.id || !info.parentID)
24017
+ return;
24018
+ const parentGoal = goals.get(info.parentID);
24019
+ if (!parentGoal)
24020
+ return;
24021
+ goals.set(info.id, {
24022
+ inheritedFrom: info.parentID,
24023
+ createdAt: Date.now(),
24024
+ text: ""
24025
+ });
24026
+ return;
24027
+ }
24028
+ if (event.type === "session.deleted") {
24029
+ const props = event.properties;
24030
+ const sessionID = props?.info?.id ?? props?.sessionID;
24031
+ if (sessionID)
24032
+ goals.delete(sessionID);
24033
+ }
24034
+ },
24035
+ handleSystemTransform: (input, output) => {
24036
+ if (!input.sessionID)
24037
+ return;
24038
+ const resolved = resolveGoal(goals, input.sessionID);
24039
+ if (!resolved)
24040
+ return;
24041
+ const agentName = options?.getAgentName?.(input.sessionID);
24042
+ const { goal, inherited } = resolved;
24043
+ if (!inherited && agentName && agentName !== "orchestrator")
24044
+ return;
24045
+ const block = formatGoal(goal, inherited);
24046
+ if (output.system.some((entry) => entry.includes(block)))
24047
+ return;
24048
+ output.system.push(block);
24049
+ },
24050
+ getGoal: (sessionID) => resolveGoal(goals, sessionID)?.goal
24051
+ };
24052
+ }
23665
24053
  // src/hooks/task-session-manager/index.ts
23666
- import path9 from "node:path";
24054
+ import path10 from "node:path";
23667
24055
  var AGENT_NAME_SET = new Set([
23668
24056
  "orchestrator",
23669
24057
  "oracle",
@@ -23688,11 +24076,11 @@ function extractPath(output) {
23688
24076
  return /<path>([^<]+)<\/path>/.exec(output)?.[1];
23689
24077
  }
23690
24078
  function normalizePath(root, file) {
23691
- const relative = path9.relative(root, file);
23692
- if (!relative || relative.startsWith("..") || path9.isAbsolute(relative)) {
24079
+ const relative2 = path10.relative(root, file);
24080
+ if (!relative2 || relative2.startsWith("..") || path10.isAbsolute(relative2)) {
23693
24081
  return file;
23694
24082
  }
23695
- return relative;
24083
+ return relative2;
23696
24084
  }
23697
24085
  function extractReadFiles(root, output) {
23698
24086
  if (typeof output.output !== "string")
@@ -24080,7 +24468,7 @@ function createTodoHygiene(options) {
24080
24468
 
24081
24469
  // src/hooks/todo-continuation/index.ts
24082
24470
  var HOOK_NAME = "todo-continuation";
24083
- var COMMAND_NAME = "auto-continue";
24471
+ var COMMAND_NAME2 = "auto-continue";
24084
24472
  var TODO_STATE_TIMEOUT_MS = 500;
24085
24473
  var CONTINUATION_PROMPT = "[Auto-continue: enabled - there are incomplete todos remaining. Continue with the next uncompleted item. Press Esc to cancel. If you need user input or review for the next item, ask instead of proceeding.]";
24086
24474
  var TODO_HYGIENE_INSTRUCTION_OPEN = '<instruction name="todo_hygiene">';
@@ -24571,7 +24959,7 @@ function createTodoContinuationHook(ctx, config) {
24571
24959
  }
24572
24960
  }
24573
24961
  async function handleCommandExecuteBefore(input, output) {
24574
- if (input.command !== COMMAND_NAME) {
24962
+ if (input.command !== COMMAND_NAME2) {
24575
24963
  return;
24576
24964
  }
24577
24965
  registerOrchestratorSession(input.sessionID);
@@ -24590,11 +24978,11 @@ function createTodoContinuationHook(ctx, config) {
24590
24978
  if (!newEnabled) {
24591
24979
  cancelPendingTimer(state);
24592
24980
  output.parts.push(createInternalAgentTextPart("[Auto-continue: disabled by user command.]"));
24593
- log(`[${HOOK_NAME}] Disabled via /${COMMAND_NAME} command`);
24981
+ log(`[${HOOK_NAME}] Disabled via /${COMMAND_NAME2} command`);
24594
24982
  return;
24595
24983
  }
24596
24984
  state.suppressUntil = 0;
24597
- log(`[${HOOK_NAME}] Enabled via /${COMMAND_NAME} command`, {
24985
+ log(`[${HOOK_NAME}] Enabled via /${COMMAND_NAME2} command`, {
24598
24986
  maxContinuations
24599
24987
  });
24600
24988
  let hasIncompleteTodos = false;
@@ -24628,7 +25016,7 @@ import path13 from "node:path";
24628
25016
  // src/interview/dashboard.ts
24629
25017
  import crypto from "node:crypto";
24630
25018
  import * as fsSync2 from "node:fs";
24631
- import fs7 from "node:fs/promises";
25019
+ import fs8 from "node:fs/promises";
24632
25020
  import {
24633
25021
  createServer
24634
25022
  } from "node:http";
@@ -24636,175 +25024,6 @@ import os4 from "node:os";
24636
25024
  import path11 from "node:path";
24637
25025
  import { URL as URL2 } from "node:url";
24638
25026
 
24639
- // src/interview/document.ts
24640
- import * as fsSync from "node:fs";
24641
- import * as fs6 from "node:fs/promises";
24642
- import * as path10 from "node:path";
24643
- var DEFAULT_OUTPUT_FOLDER = "interview";
24644
- function normalizeOutputFolder(outputFolder) {
24645
- const normalized = outputFolder.trim().replace(/^\/+|\/+$/g, "");
24646
- return normalized || DEFAULT_OUTPUT_FOLDER;
24647
- }
24648
- function createInterviewDirectoryPath(directory, outputFolder) {
24649
- return path10.join(directory, normalizeOutputFolder(outputFolder));
24650
- }
24651
- function createInterviewFilePath(directory, outputFolder, idea) {
24652
- const fileName = `${slugify(idea) || "interview"}.md`;
24653
- return path10.join(createInterviewDirectoryPath(directory, outputFolder), fileName);
24654
- }
24655
- function relativeInterviewPath(directory, filePath) {
24656
- return path10.relative(directory, filePath) || path10.basename(filePath);
24657
- }
24658
- function resolveExistingInterviewPath(directory, outputFolder, value) {
24659
- const trimmed = value.trim();
24660
- if (!trimmed) {
24661
- return null;
24662
- }
24663
- const outputDir = createInterviewDirectoryPath(directory, outputFolder);
24664
- const candidates = new Set;
24665
- const resolvedRoot = path10.resolve(directory);
24666
- if (path10.isAbsolute(trimmed)) {
24667
- candidates.add(trimmed);
24668
- } else {
24669
- candidates.add(path10.resolve(directory, trimmed));
24670
- candidates.add(path10.join(outputDir, trimmed));
24671
- if (!trimmed.endsWith(".md")) {
24672
- candidates.add(path10.join(outputDir, `${trimmed}.md`));
24673
- }
24674
- }
24675
- for (const candidate of candidates) {
24676
- if (path10.extname(candidate) !== ".md") {
24677
- continue;
24678
- }
24679
- const resolved = path10.resolve(candidate);
24680
- if (!resolved.startsWith(resolvedRoot + path10.sep) && resolved !== resolvedRoot) {
24681
- continue;
24682
- }
24683
- if (fsSync.existsSync(candidate)) {
24684
- return candidate;
24685
- }
24686
- }
24687
- return null;
24688
- }
24689
- function slugify(value) {
24690
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
24691
- }
24692
- function extractHistorySection(document) {
24693
- const marker = `## Q&A history
24694
-
24695
- `;
24696
- const index = document.indexOf(marker);
24697
- return index >= 0 ? document.slice(index + marker.length).trim() : "";
24698
- }
24699
- function extractSummarySection(document) {
24700
- const marker = `## Current spec
24701
-
24702
- `;
24703
- const historyMarker = `
24704
-
24705
- ## Q&A history`;
24706
- const start = document.indexOf(marker);
24707
- if (start < 0) {
24708
- return "";
24709
- }
24710
- const summaryStart = start + marker.length;
24711
- const summaryEnd = document.indexOf(historyMarker, summaryStart);
24712
- return document.slice(summaryStart, summaryEnd >= 0 ? summaryEnd : undefined).trim();
24713
- }
24714
- function extractTitle(document) {
24715
- const match = document.match(/^#\s+(.+)$/m);
24716
- return match?.[1]?.trim() ?? "";
24717
- }
24718
- function buildInterviewDocument(idea, summary, history, meta) {
24719
- const normalizedSummary = summary.trim() || "Waiting for interview answers.";
24720
- const normalizedHistory = history.trim() || "No answers yet.";
24721
- const frontmatter = meta?.sessionID ? [
24722
- "---",
24723
- `sessionID: ${meta.sessionID}`,
24724
- `baseMessageCount: ${meta.baseMessageCount ?? 0}`,
24725
- `updatedAt: ${new Date().toISOString()}`,
24726
- "---",
24727
- ""
24728
- ].join(`
24729
- `) : "";
24730
- return [
24731
- frontmatter,
24732
- `# ${idea}`,
24733
- "",
24734
- "## Current spec",
24735
- "",
24736
- normalizedSummary,
24737
- "",
24738
- "## Q&A history",
24739
- "",
24740
- normalizedHistory,
24741
- ""
24742
- ].join(`
24743
- `);
24744
- }
24745
- function parseFrontmatter(content) {
24746
- const match = content.match(/^---\n([\s\S]*?)\n---\n/);
24747
- if (!match)
24748
- return null;
24749
- const result = {};
24750
- for (const line of match[1].split(`
24751
- `)) {
24752
- const colonIdx = line.indexOf(":");
24753
- if (colonIdx > 0) {
24754
- result[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
24755
- }
24756
- }
24757
- return result;
24758
- }
24759
- async function ensureInterviewFile(record) {
24760
- await fs6.mkdir(path10.dirname(record.markdownPath), { recursive: true });
24761
- try {
24762
- await fs6.access(record.markdownPath);
24763
- } catch {
24764
- await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, "", "", {
24765
- sessionID: record.sessionID,
24766
- baseMessageCount: record.baseMessageCount
24767
- }), "utf8");
24768
- }
24769
- }
24770
- async function readInterviewDocument(record) {
24771
- try {
24772
- return await fs6.readFile(record.markdownPath, "utf8");
24773
- } catch {}
24774
- await ensureInterviewFile(record);
24775
- return fs6.readFile(record.markdownPath, "utf8");
24776
- }
24777
- async function rewriteInterviewDocument(record, summary) {
24778
- const existing = await readInterviewDocument(record);
24779
- const history = extractHistorySection(existing);
24780
- const next = buildInterviewDocument(record.idea, summary, history, {
24781
- sessionID: record.sessionID,
24782
- baseMessageCount: record.baseMessageCount
24783
- });
24784
- await fs6.writeFile(record.markdownPath, next, "utf8");
24785
- return next;
24786
- }
24787
- async function appendInterviewAnswers(record, questions, answers) {
24788
- const existing = await readInterviewDocument(record);
24789
- const summary = extractSummarySection(existing);
24790
- const history = extractHistorySection(existing);
24791
- const questionMap = new Map(questions.map((question) => [question.id, question]));
24792
- const appended = answers.map((answer) => {
24793
- const question = questionMap.get(answer.questionId);
24794
- return question ? `Q: ${question.question}
24795
- A: ${answer.answer.trim()}` : null;
24796
- }).filter((value) => value !== null).join(`
24797
-
24798
- `);
24799
- const nextHistory = [history === "No answers yet." ? "" : history, appended].filter(Boolean).join(`
24800
-
24801
- `);
24802
- await fs6.writeFile(record.markdownPath, buildInterviewDocument(record.idea, summary, nextHistory, {
24803
- sessionID: record.sessionID,
24804
- baseMessageCount: record.baseMessageCount
24805
- }), "utf8");
24806
- }
24807
-
24808
25027
  // src/interview/helpers.ts
24809
25028
  function sendJson(response, status, value) {
24810
25029
  response.statusCode = status;
@@ -26449,7 +26668,7 @@ function removeAuthFile(port) {
26449
26668
  }
26450
26669
  async function readDashboardAuthFile(port) {
26451
26670
  try {
26452
- const content = await fs7.readFile(getAuthFilePath(port), "utf8");
26671
+ const content = await fs8.readFile(getAuthFilePath(port), "utf8");
26453
26672
  const data = JSON.parse(content);
26454
26673
  try {
26455
26674
  process.kill(data.pid, 0);
@@ -26572,7 +26791,7 @@ function createDashboardServer(config) {
26572
26791
  const interviewDir = path11.join(dir, config.outputFolder);
26573
26792
  let entries;
26574
26793
  try {
26575
- entries = await fs7.readdir(interviewDir);
26794
+ entries = await fs8.readdir(interviewDir);
26576
26795
  } catch {
26577
26796
  continue;
26578
26797
  }
@@ -26581,7 +26800,7 @@ function createDashboardServer(config) {
26581
26800
  continue;
26582
26801
  let content;
26583
26802
  try {
26584
- content = await fs7.readFile(path11.join(interviewDir, entry), "utf8");
26803
+ content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
26585
26804
  } catch {
26586
26805
  continue;
26587
26806
  }
@@ -26610,7 +26829,7 @@ function createDashboardServer(config) {
26610
26829
  const interviewDir = path11.join(dir, config.outputFolder);
26611
26830
  let entries;
26612
26831
  try {
26613
- entries = await fs7.readdir(interviewDir);
26832
+ entries = await fs8.readdir(interviewDir);
26614
26833
  } catch {
26615
26834
  continue;
26616
26835
  }
@@ -26619,7 +26838,7 @@ function createDashboardServer(config) {
26619
26838
  continue;
26620
26839
  let content;
26621
26840
  try {
26622
- content = await fs7.readFile(path11.join(interviewDir, entry), "utf8");
26841
+ content = await fs8.readFile(path11.join(interviewDir, entry), "utf8");
26623
26842
  } catch {
26624
26843
  continue;
26625
26844
  }
@@ -26903,7 +27122,7 @@ function createDashboardServer(config) {
26903
27122
  let markdownPath = entry.filePath;
26904
27123
  if (entry.filePath) {
26905
27124
  try {
26906
- document = await fs7.readFile(entry.filePath, "utf8");
27125
+ document = await fs8.readFile(entry.filePath, "utf8");
26907
27126
  } catch {}
26908
27127
  } else {
26909
27128
  const dirs = getKnownDirectories();
@@ -26911,7 +27130,7 @@ function createDashboardServer(config) {
26911
27130
  const slug = extractResumeSlug(interviewId);
26912
27131
  const candidate = path11.join(dir, config.outputFolder, `${slug}.md`);
26913
27132
  try {
26914
- document = await fs7.readFile(candidate, "utf8");
27133
+ document = await fs8.readFile(candidate, "utf8");
26915
27134
  markdownPath = candidate;
26916
27135
  entry.filePath = candidate;
26917
27136
  break;
@@ -27436,7 +27655,7 @@ function createInterviewServer(deps) {
27436
27655
 
27437
27656
  // src/interview/service.ts
27438
27657
  import { spawn as spawn2 } from "node:child_process";
27439
- import * as fs8 from "node:fs/promises";
27658
+ import * as fs9 from "node:fs/promises";
27440
27659
  import * as path12 from "node:path";
27441
27660
 
27442
27661
  // src/interview/types.ts
@@ -27609,7 +27828,7 @@ function buildAnswerPrompt(answers, questions, maxQuestions) {
27609
27828
  }
27610
27829
 
27611
27830
  // src/interview/service.ts
27612
- var COMMAND_NAME2 = "interview";
27831
+ var COMMAND_NAME3 = "interview";
27613
27832
  var DEFAULT_MAX_QUESTIONS = 2;
27614
27833
  function isTruthyEnvFlag(value) {
27615
27834
  if (!value) {
@@ -27711,11 +27930,11 @@ function createInterviewService(ctx, config, deps) {
27711
27930
  const dir = path12.dirname(interview.markdownPath);
27712
27931
  const newPath = path12.join(dir, `${newSlug}.md`);
27713
27932
  try {
27714
- await fs8.access(newPath);
27933
+ await fs9.access(newPath);
27715
27934
  return;
27716
27935
  } catch {}
27717
27936
  try {
27718
- await fs8.rename(interview.markdownPath, newPath);
27937
+ await fs9.rename(interview.markdownPath, newPath);
27719
27938
  interview.markdownPath = newPath;
27720
27939
  log("[interview] renamed file with assistant title:", {
27721
27940
  from: currentFileName,
@@ -27781,7 +28000,7 @@ function createInterviewService(ctx, config, deps) {
27781
28000
  active.status = "abandoned";
27782
28001
  }
27783
28002
  }
27784
- const document = await fs8.readFile(markdownPath, "utf8");
28003
+ const document = await fs9.readFile(markdownPath, "utf8");
27785
28004
  const messages = await loadMessages(sessionID);
27786
28005
  const title = extractTitle(document);
27787
28006
  const record = {
@@ -27856,11 +28075,11 @@ function createInterviewService(ctx, config, deps) {
27856
28075
  }
27857
28076
  function registerCommand(opencodeConfig) {
27858
28077
  const configCommand = opencodeConfig.command;
27859
- if (!configCommand?.[COMMAND_NAME2]) {
28078
+ if (!configCommand?.[COMMAND_NAME3]) {
27860
28079
  if (!opencodeConfig.command) {
27861
28080
  opencodeConfig.command = {};
27862
28081
  }
27863
- opencodeConfig.command[COMMAND_NAME2] = {
28082
+ opencodeConfig.command[COMMAND_NAME3] = {
27864
28083
  template: "Start an interview and write a live markdown spec",
27865
28084
  description: "Open a localhost interview UI linked to the current OpenCode session"
27866
28085
  };
@@ -27934,7 +28153,7 @@ function createInterviewService(ctx, config, deps) {
27934
28153
  }
27935
28154
  }
27936
28155
  async function handleCommandExecuteBefore(input, output) {
27937
- if (input.command !== COMMAND_NAME2) {
28156
+ if (input.command !== COMMAND_NAME3) {
27938
28157
  return;
27939
28158
  }
27940
28159
  const idea = input.arguments.trim();
@@ -27953,7 +28172,7 @@ function createInterviewService(ctx, config, deps) {
27953
28172
  const resumePath = resolveExistingInterviewPath(ctx.directory, outputFolder, idea);
27954
28173
  if (resumePath) {
27955
28174
  const interview2 = await resumeInterview(input.sessionID, resumePath);
27956
- const document = await fs8.readFile(interview2.markdownPath, "utf8");
28175
+ const document = await fs9.readFile(interview2.markdownPath, "utf8");
27957
28176
  await notifyInterviewUrl(input.sessionID, interview2);
27958
28177
  output.parts.push(createInternalAgentTextPart(buildResumePrompt(document, maxQuestions)));
27959
28178
  return;
@@ -28017,7 +28236,7 @@ function createInterviewService(ctx, config, deps) {
28017
28236
  const activePaths = new Set([...interviewsById.values()].filter((i) => i.status === "active").map((i) => path12.resolve(i.markdownPath)));
28018
28237
  let entries;
28019
28238
  try {
28020
- entries = await fs8.readdir(outputDir);
28239
+ entries = await fs9.readdir(outputDir);
28021
28240
  } catch {
28022
28241
  return [];
28023
28242
  }
@@ -28030,7 +28249,7 @@ function createInterviewService(ctx, config, deps) {
28030
28249
  continue;
28031
28250
  let content;
28032
28251
  try {
28033
- content = await fs8.readFile(fullPath, "utf8");
28252
+ content = await fs9.readFile(fullPath, "utf8");
28034
28253
  } catch {
28035
28254
  continue;
28036
28255
  }
@@ -28644,7 +28863,11 @@ class TmuxMultiplexer {
28644
28863
  this.storedLayout = layout;
28645
28864
  this.storedMainPaneSize = mainPaneSize;
28646
28865
  try {
28647
- const layoutResult = await this.runTmux(tmux, ["select-layout", ...this.targetArgs(), layout]);
28866
+ const layoutResult = await this.runTmux(tmux, [
28867
+ "select-layout",
28868
+ ...this.targetArgs(),
28869
+ layout
28870
+ ]);
28648
28871
  if (layoutResult !== 0)
28649
28872
  return;
28650
28873
  if (layout === "main-horizontal" || layout === "main-vertical") {
@@ -28657,7 +28880,11 @@ class TmuxMultiplexer {
28657
28880
  ]);
28658
28881
  if (sizeResult !== 0)
28659
28882
  return;
28660
- const reapplyResult = await this.runTmux(tmux, ["select-layout", ...this.targetArgs(), layout]);
28883
+ const reapplyResult = await this.runTmux(tmux, [
28884
+ "select-layout",
28885
+ ...this.targetArgs(),
28886
+ layout
28887
+ ]);
28661
28888
  if (reapplyResult !== 0)
28662
28889
  return;
28663
28890
  }
@@ -29095,29 +29322,46 @@ function startAvailabilityCheck(config) {
29095
29322
  // src/multiplexer/session-manager.ts
29096
29323
  var SESSION_TIMEOUT_MS = 10 * 60 * 1000;
29097
29324
  var SESSION_MISSING_GRACE_MS = POLL_INTERVAL_BACKGROUND_MS * 3;
29098
-
29325
+ var SHARED_STATE_KEY = Symbol.for("oh-my-opencode-slim.multiplexer-session-manager.state");
29326
+ function getSharedState() {
29327
+ const globalWithState = globalThis;
29328
+ globalWithState[SHARED_STATE_KEY] ??= {
29329
+ sessions: new Map,
29330
+ knownSessions: new Map,
29331
+ spawningSessions: new Set,
29332
+ closingSessions: new Map
29333
+ };
29334
+ return globalWithState[SHARED_STATE_KEY];
29335
+ }
29099
29336
  class MultiplexerSessionManager {
29100
- client;
29337
+ instanceId = Math.random().toString(36).slice(2, 8);
29101
29338
  serverUrl;
29102
29339
  directory;
29103
29340
  multiplexer = null;
29104
- sessions = new Map;
29105
- knownSessions = new Map;
29106
- spawningSessions = new Set;
29107
- closingSessions = new Map;
29341
+ sessions;
29342
+ knownSessions;
29343
+ spawningSessions;
29344
+ closingSessions;
29108
29345
  pollInterval;
29109
29346
  enabled = false;
29110
29347
  constructor(ctx, config) {
29111
- this.client = ctx.client;
29348
+ const sharedState = getSharedState();
29349
+ this.sessions = sharedState.sessions;
29350
+ this.knownSessions = sharedState.knownSessions;
29351
+ this.spawningSessions = sharedState.spawningSessions;
29352
+ this.closingSessions = sharedState.closingSessions;
29112
29353
  this.directory = ctx.directory;
29113
29354
  const defaultPort = process.env.OPENCODE_PORT ?? "4096";
29114
29355
  this.serverUrl = ctx.serverUrl?.toString() ?? `http://localhost:${defaultPort}`;
29115
29356
  this.multiplexer = getMultiplexer(config);
29116
29357
  this.enabled = config.type !== "none" && this.multiplexer !== null && this.multiplexer.isInsideSession();
29117
29358
  log("[multiplexer-session-manager] initialized", {
29359
+ instanceId: this.instanceId,
29118
29360
  enabled: this.enabled,
29119
29361
  type: config.type,
29120
- serverUrl: this.serverUrl
29362
+ serverUrl: this.serverUrl,
29363
+ trackedSessions: this.sessions.size,
29364
+ knownSessions: this.knownSessions.size
29121
29365
  });
29122
29366
  }
29123
29367
  async onSessionCreated(event) {
@@ -29135,6 +29379,7 @@ class MultiplexerSessionManager {
29135
29379
  const directory = info.directory ?? this.directory;
29136
29380
  if (this.isTrackedOrSpawning(sessionId)) {
29137
29381
  log("[multiplexer-session-manager] session already tracked or spawning", {
29382
+ instanceId: this.instanceId,
29138
29383
  sessionId
29139
29384
  });
29140
29385
  return;
@@ -29154,6 +29399,7 @@ class MultiplexerSessionManager {
29154
29399
  const serverRunning = await isServerRunning(this.serverUrl);
29155
29400
  if (!serverRunning) {
29156
29401
  log("[multiplexer-session-manager] server not running, skipping", {
29402
+ instanceId: this.instanceId,
29157
29403
  serverUrl: this.serverUrl
29158
29404
  });
29159
29405
  return;
@@ -29164,10 +29410,12 @@ class MultiplexerSessionManager {
29164
29410
  log("[multiplexer-session-manager] child session created, spawning pane", {
29165
29411
  sessionId,
29166
29412
  parentId,
29167
- title
29413
+ title,
29414
+ instanceId: this.instanceId
29168
29415
  });
29169
29416
  const paneResult = await this.multiplexer.spawnPane(sessionId, title, this.serverUrl, directory).catch((err) => {
29170
29417
  log("[multiplexer-session-manager] failed to spawn pane", {
29418
+ instanceId: this.instanceId,
29171
29419
  error: String(err)
29172
29420
  });
29173
29421
  return { success: false, paneId: undefined };
@@ -29178,6 +29426,7 @@ class MultiplexerSessionManager {
29178
29426
  await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale spawned pane failed", {
29179
29427
  sessionId,
29180
29428
  paneId: paneResult.paneId,
29429
+ instanceId: this.instanceId,
29181
29430
  error: String(err)
29182
29431
  }));
29183
29432
  return;
@@ -29193,6 +29442,7 @@ class MultiplexerSessionManager {
29193
29442
  lastSeenAt: now
29194
29443
  });
29195
29444
  log("[multiplexer-session-manager] pane spawned", {
29445
+ instanceId: this.instanceId,
29196
29446
  sessionId,
29197
29447
  paneId: paneResult.paneId
29198
29448
  });
@@ -29204,6 +29454,19 @@ class MultiplexerSessionManager {
29204
29454
  async onSessionStatus(event) {
29205
29455
  if (!this.enabled)
29206
29456
  return;
29457
+ if (event.type === "session.idle") {
29458
+ const sessionId2 = event.properties?.sessionID;
29459
+ if (!sessionId2)
29460
+ return;
29461
+ log("[multiplexer-session-manager] session idle event received", {
29462
+ instanceId: this.instanceId,
29463
+ sessionId: sessionId2,
29464
+ tracked: this.sessions.has(sessionId2),
29465
+ known: this.knownSessions.has(sessionId2)
29466
+ });
29467
+ await this.closeSession(sessionId2, "idle");
29468
+ return;
29469
+ }
29207
29470
  if (event.type !== "session.status")
29208
29471
  return;
29209
29472
  const sessionId = event.properties?.sessionID;
@@ -29214,6 +29477,12 @@ class MultiplexerSessionManager {
29214
29477
  return;
29215
29478
  }
29216
29479
  if (event.properties?.status?.type === "busy") {
29480
+ log("[multiplexer-session-manager] session busy event received", {
29481
+ instanceId: this.instanceId,
29482
+ sessionId,
29483
+ tracked: this.sessions.has(sessionId),
29484
+ known: this.knownSessions.has(sessionId)
29485
+ });
29217
29486
  await this.respawnIfKnown(sessionId);
29218
29487
  }
29219
29488
  }
@@ -29226,6 +29495,7 @@ class MultiplexerSessionManager {
29226
29495
  if (!sessionId)
29227
29496
  return;
29228
29497
  log("[multiplexer-session-manager] session deleted, closing pane", {
29498
+ instanceId: this.instanceId,
29229
29499
  sessionId
29230
29500
  });
29231
29501
  await this.closeSession(sessionId, "deleted");
@@ -29234,13 +29504,17 @@ class MultiplexerSessionManager {
29234
29504
  if (this.pollInterval)
29235
29505
  return;
29236
29506
  this.pollInterval = setInterval(() => this.pollSessions(), POLL_INTERVAL_BACKGROUND_MS);
29237
- log("[multiplexer-session-manager] polling started");
29507
+ log("[multiplexer-session-manager] polling started", {
29508
+ instanceId: this.instanceId
29509
+ });
29238
29510
  }
29239
29511
  stopPolling() {
29240
29512
  if (this.pollInterval) {
29241
29513
  clearInterval(this.pollInterval);
29242
29514
  this.pollInterval = undefined;
29243
- log("[multiplexer-session-manager] polling stopped");
29515
+ log("[multiplexer-session-manager] polling stopped", {
29516
+ instanceId: this.instanceId
29517
+ });
29244
29518
  }
29245
29519
  }
29246
29520
  async pollSessions() {
@@ -29249,8 +29523,7 @@ class MultiplexerSessionManager {
29249
29523
  return;
29250
29524
  }
29251
29525
  try {
29252
- const statusResult = await this.client.session.status();
29253
- const allStatuses = statusResult.data ?? {};
29526
+ const allStatuses = await this.fetchSessionStatuses();
29254
29527
  const now = Date.now();
29255
29528
  const sessionsToClose = [];
29256
29529
  for (const [sessionId, tracked] of this.sessions.entries()) {
@@ -29278,6 +29551,14 @@ class MultiplexerSessionManager {
29278
29551
  log("[multiplexer-session-manager] poll error", { error: String(err) });
29279
29552
  }
29280
29553
  }
29554
+ async fetchSessionStatuses() {
29555
+ const url = new URL("/session/status", this.serverUrl);
29556
+ const response = await fetch(url, { signal: AbortSignal.timeout(2000) });
29557
+ if (!response.ok) {
29558
+ throw new Error(`session status request failed: ${response.status} ${response.statusText}`);
29559
+ }
29560
+ return await response.json();
29561
+ }
29281
29562
  async closeSession(sessionId, reason) {
29282
29563
  if (reason === "deleted") {
29283
29564
  this.knownSessions.delete(sessionId);
@@ -29286,10 +29567,19 @@ class MultiplexerSessionManager {
29286
29567
  if (existingClose)
29287
29568
  return existingClose;
29288
29569
  const tracked = this.sessions.get(sessionId);
29289
- if (!tracked || !this.multiplexer)
29570
+ if (!tracked || !this.multiplexer) {
29571
+ log("[multiplexer-session-manager] close skipped; session not tracked", {
29572
+ instanceId: this.instanceId,
29573
+ sessionId,
29574
+ reason,
29575
+ tracked: !!tracked,
29576
+ hasMultiplexer: !!this.multiplexer
29577
+ });
29290
29578
  return;
29579
+ }
29291
29580
  this.sessions.delete(sessionId);
29292
29581
  log("[multiplexer-session-manager] closing session pane", {
29582
+ instanceId: this.instanceId,
29293
29583
  sessionId,
29294
29584
  paneId: tracked.paneId,
29295
29585
  reason
@@ -29297,6 +29587,7 @@ class MultiplexerSessionManager {
29297
29587
  const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
29298
29588
  return;
29299
29589
  }).catch((err) => log("[multiplexer-session-manager] failed to close session pane", {
29590
+ instanceId: this.instanceId,
29300
29591
  sessionId,
29301
29592
  paneId: tracked.paneId,
29302
29593
  reason,
@@ -29325,6 +29616,7 @@ class MultiplexerSessionManager {
29325
29616
  const serverRunning = await isServerRunning(this.serverUrl);
29326
29617
  if (!serverRunning) {
29327
29618
  log("[multiplexer-session-manager] server not running, skipping busy respawn", {
29619
+ instanceId: this.instanceId,
29328
29620
  serverUrl: this.serverUrl,
29329
29621
  sessionId
29330
29622
  });
@@ -29334,12 +29626,14 @@ class MultiplexerSessionManager {
29334
29626
  return;
29335
29627
  }
29336
29628
  log("[multiplexer-session-manager] child session busy again, respawning pane", {
29629
+ instanceId: this.instanceId,
29337
29630
  sessionId,
29338
29631
  parentId: known.parentId,
29339
29632
  title: known.title
29340
29633
  });
29341
29634
  const paneResult = await this.multiplexer.spawnPane(sessionId, known.title, this.serverUrl, known.directory).catch((err) => {
29342
29635
  log("[multiplexer-session-manager] failed to respawn pane", {
29636
+ instanceId: this.instanceId,
29343
29637
  error: String(err)
29344
29638
  });
29345
29639
  return { success: false, paneId: undefined };
@@ -29348,6 +29642,7 @@ class MultiplexerSessionManager {
29348
29642
  return;
29349
29643
  if (!this.knownSessions.has(sessionId) || this.closingSessions.has(sessionId)) {
29350
29644
  await this.multiplexer.closePane(paneResult.paneId).catch((err) => log("[multiplexer-session-manager] closing stale respawned pane failed", {
29645
+ instanceId: this.instanceId,
29351
29646
  sessionId,
29352
29647
  paneId: paneResult.paneId,
29353
29648
  error: String(err)
@@ -29365,6 +29660,7 @@ class MultiplexerSessionManager {
29365
29660
  lastSeenAt: now
29366
29661
  });
29367
29662
  log("[multiplexer-session-manager] pane respawned on busy", {
29663
+ instanceId: this.instanceId,
29368
29664
  sessionId,
29369
29665
  paneId: paneResult.paneId
29370
29666
  });
@@ -30043,7 +30339,7 @@ Returns the councillor responses with a summary footer.`,
30043
30339
  return { council_session };
30044
30340
  }
30045
30341
  // src/tui-state.ts
30046
- import * as fs9 from "node:fs";
30342
+ import * as fs10 from "node:fs";
30047
30343
  import * as os5 from "node:os";
30048
30344
  import * as path14 from "node:path";
30049
30345
  var STATE_DIR = "oh-my-opencode-slim";
@@ -30073,14 +30369,14 @@ function parseSnapshot(value) {
30073
30369
  }
30074
30370
  function readTuiSnapshot() {
30075
30371
  try {
30076
- return parseSnapshot(fs9.readFileSync(getTuiStatePath(), "utf8"));
30372
+ return parseSnapshot(fs10.readFileSync(getTuiStatePath(), "utf8"));
30077
30373
  } catch {
30078
30374
  return emptySnapshot();
30079
30375
  }
30080
30376
  }
30081
30377
  async function readTuiSnapshotAsync() {
30082
30378
  try {
30083
- return parseSnapshot(await fs9.promises.readFile(getTuiStatePath(), "utf8"));
30379
+ return parseSnapshot(await fs10.promises.readFile(getTuiStatePath(), "utf8"));
30084
30380
  } catch {
30085
30381
  return emptySnapshot();
30086
30382
  }
@@ -30088,8 +30384,8 @@ async function readTuiSnapshotAsync() {
30088
30384
  function writeTuiSnapshot(snapshot) {
30089
30385
  try {
30090
30386
  const filePath = getTuiStatePath();
30091
- fs9.mkdirSync(path14.dirname(filePath), { recursive: true });
30092
- fs9.writeFileSync(filePath, `${JSON.stringify(snapshot)}
30387
+ fs10.mkdirSync(path14.dirname(filePath), { recursive: true });
30388
+ fs10.writeFileSync(filePath, `${JSON.stringify(snapshot)}
30093
30389
  `);
30094
30390
  } catch {}
30095
30391
  }
@@ -30111,11 +30407,11 @@ function recordTuiAgentModel(input) {
30111
30407
  }
30112
30408
 
30113
30409
  // src/tools/preset-manager.ts
30114
- var COMMAND_NAME3 = "preset";
30410
+ var COMMAND_NAME4 = "preset";
30115
30411
  function createPresetManager(ctx, config) {
30116
30412
  let activePreset = getActiveRuntimePreset() ?? config.preset ?? null;
30117
30413
  async function handleCommandExecuteBefore(input, output) {
30118
- if (input.command !== COMMAND_NAME3) {
30414
+ if (input.command !== COMMAND_NAME4) {
30119
30415
  return;
30120
30416
  }
30121
30417
  output.parts.length = 0;
@@ -30134,11 +30430,11 @@ function createPresetManager(ctx, config) {
30134
30430
  }
30135
30431
  function registerCommand(opencodeConfig) {
30136
30432
  const configCommand = opencodeConfig.command;
30137
- if (!configCommand?.[COMMAND_NAME3]) {
30433
+ if (!configCommand?.[COMMAND_NAME4]) {
30138
30434
  if (!opencodeConfig.command) {
30139
30435
  opencodeConfig.command = {};
30140
30436
  }
30141
- opencodeConfig.command[COMMAND_NAME3] = {
30437
+ opencodeConfig.command[COMMAND_NAME4] = {
30142
30438
  template: "List available presets and switch between them",
30143
30439
  description: "Switch agent presets at runtime (e.g., /preset cheap, /preset powerful)"
30144
30440
  };
@@ -30350,21 +30646,21 @@ async function saveBinary(binaryDir, data, contentType, filename) {
30350
30646
  throw new Error("Unable to allocate unique filename for binary content");
30351
30647
  }
30352
30648
 
30353
- // node_modules/lru-cache/dist/esm/node/index.min.js
30354
- import { tracingChannel as j, channel as I } from "node:diagnostics_channel";
30355
- var S = I("lru-cache:metrics");
30356
- var W = j("lru-cache");
30649
+ // node_modules/.pnpm/lru-cache@11.3.6/node_modules/lru-cache/dist/esm/node/index.min.js
30650
+ import { tracingChannel as I, channel as G } from "node:diagnostics_channel";
30651
+ var S = G("lru-cache:metrics");
30652
+ var W = I("lru-cache");
30653
+ var C = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
30357
30654
  var D = () => S.hasSubscribers || W.hasSubscribers;
30358
- var G = typeof performance == "object" && performance && typeof performance.now == "function" ? performance : Date;
30359
- var M = new Set;
30360
- var C = typeof process == "object" && process ? process : {};
30655
+ var U = new Set;
30656
+ var L = typeof process == "object" && process ? process : {};
30361
30657
  var P = (u, e, t, i) => {
30362
- typeof C.emitWarning == "function" ? C.emitWarning(u, e, t, i) : console.error(`[${t}] ${e}: ${u}`);
30658
+ typeof L.emitWarning == "function" ? L.emitWarning(u, e, t, i) : console.error(`[${t}] ${e}: ${u}`);
30363
30659
  };
30364
- var H = (u) => !M.has(u);
30365
- var $ = Symbol("type");
30660
+ var H = (u) => !U.has(u);
30661
+ var X = Symbol("type");
30366
30662
  var F = (u) => !!u && u === Math.floor(u) && u > 0 && isFinite(u);
30367
- var U = (u) => F(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null;
30663
+ var j = (u) => F(u) ? u <= Math.pow(2, 8) ? Uint8Array : u <= Math.pow(2, 16) ? Uint16Array : u <= Math.pow(2, 32) ? Uint32Array : u <= Number.MAX_SAFE_INTEGER ? O : null : null;
30368
30664
  var O = class extends Array {
30369
30665
  constructor(e) {
30370
30666
  super(e), this.fill(0);
@@ -30375,7 +30671,7 @@ var R = class u {
30375
30671
  length;
30376
30672
  static #o = false;
30377
30673
  static create(e) {
30378
- let t = U(e);
30674
+ let t = j(e);
30379
30675
  if (!t)
30380
30676
  return [];
30381
30677
  u.#o = true;
@@ -30394,11 +30690,11 @@ var R = class u {
30394
30690
  return this.heap[--this.length];
30395
30691
  }
30396
30692
  };
30397
- var L = class u2 {
30693
+ var M = class u2 {
30398
30694
  #o;
30399
30695
  #u;
30400
30696
  #w;
30401
- #D;
30697
+ #x;
30402
30698
  #S;
30403
30699
  #M;
30404
30700
  #U;
@@ -30469,7 +30765,7 @@ var L = class u2 {
30469
30765
  return this.#w;
30470
30766
  }
30471
30767
  get onInsert() {
30472
- return this.#D;
30768
+ return this.#x;
30473
30769
  }
30474
30770
  get disposeAfter() {
30475
30771
  return this.#S;
@@ -30478,9 +30774,9 @@ var L = class u2 {
30478
30774
  let { max: t = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z5, perf: x } = e;
30479
30775
  if (x !== undefined && typeof x?.now != "function")
30480
30776
  throw new TypeError("perf option must have a now() method if specified");
30481
- if (this.#m = x ?? G, t !== 0 && !F(t))
30777
+ if (this.#m = x ?? C, t !== 0 && !F(t))
30482
30778
  throw new TypeError("max option must be a nonnegative integer");
30483
- let v = t ? U(t) : Array;
30779
+ let v = t ? j(t) : Array;
30484
30780
  if (!v)
30485
30781
  throw new Error("invalid max value: " + t);
30486
30782
  if (this.#o = t, this.#u = T, this.maxEntrySize = w || this.#u, this.sizeCalculation = y, this.sizeCalculation) {
@@ -30493,7 +30789,7 @@ var L = class u2 {
30493
30789
  throw new TypeError("memoMethod must be a function if defined");
30494
30790
  if (this.#U = m, a !== undefined && typeof a != "function")
30495
30791
  throw new TypeError("fetchMethod must be a function if specified");
30496
- if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#D = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#D, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z5, this.maxEntrySize !== 0) {
30792
+ if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#x = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#x, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z5, this.maxEntrySize !== 0) {
30497
30793
  if (this.#u !== 0 && !F(this.#u))
30498
30794
  throw new TypeError("maxSize must be a positive integer if specified");
30499
30795
  if (!F(this.maxEntrySize))
@@ -30509,7 +30805,7 @@ var L = class u2 {
30509
30805
  throw new TypeError("At least one of max, maxSize, or ttl is required");
30510
30806
  if (!this.ttlAutopurge && !this.#o && !this.#u) {
30511
30807
  let E = "LRU_CACHE_UNBOUNDED";
30512
- H(E) && (M.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
30808
+ H(E) && (U.add(E), P("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", E, u2));
30513
30809
  }
30514
30810
  }
30515
30811
  getRemainingTTL(e) {
@@ -30521,7 +30817,7 @@ var L = class u2 {
30521
30817
  let i = this.ttlAutopurge ? Array.from({ length: this.#o }) : undefined;
30522
30818
  this.#g = i, this.#N = (r, h, l = this.#m.now()) => {
30523
30819
  t[r] = h !== 0 ? l : 0, e[r] = h, s(r, h);
30524
- }, this.#x = (r) => {
30820
+ }, this.#D = (r) => {
30525
30821
  t[r] = e[r] !== 0 ? this.#m.now() : 0, s(r, e[r]);
30526
30822
  };
30527
30823
  let s = this.ttlAutopurge ? (r, h) => {
@@ -30565,7 +30861,7 @@ var L = class u2 {
30565
30861
  return !!l && !!h && (n || o()) - h > l;
30566
30862
  };
30567
30863
  }
30568
- #x = () => {};
30864
+ #D = () => {};
30569
30865
  #E = () => {};
30570
30866
  #N = () => {};
30571
30867
  #p = () => false;
@@ -30731,7 +31027,7 @@ var L = class u2 {
30731
31027
  return this.#v(e, "set"), h && (h.set = "miss", h.maxEntrySizeExceeded = true), this;
30732
31028
  let f = this.#n === 0 ? undefined : this.#s.get(e);
30733
31029
  if (f === undefined)
30734
- f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#D?.(t, e, "add");
31030
+ f = this.#n === 0 ? this.#h : this.#y.length !== 0 ? this.#y.pop() : this.#n === this.#o ? this.#G(false) : this.#n, this.#i[f] = e, this.#t[f] = t, this.#s.set(e, f), this.#a[this.#h] = f, this.#c[f] = this.#h, this.#h = f, this.#n++, this.#I(f, c, h), h && (h.set = "add"), l = false, this.#j && this.#x?.(t, e, "add");
30735
31031
  else {
30736
31032
  this.#L(f);
30737
31033
  let g = this.#t[f];
@@ -30795,7 +31091,7 @@ var L = class u2 {
30795
31091
  if (this.#p(n))
30796
31092
  s && (s.has = "stale", this.#E(s, n));
30797
31093
  else
30798
- return i && this.#x(n), s && (s.has = "hit", this.#E(s, n)), true;
31094
+ return i && this.#D(n), s && (s.has = "hit", this.#E(s, n)), true;
30799
31095
  } else
30800
31096
  s && (s.has = "miss");
30801
31097
  return false;
@@ -30853,7 +31149,7 @@ var L = class u2 {
30853
31149
  let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
30854
31150
  t.status = s, s && t.context && (s.context = t.context);
30855
31151
  let n = this.#B(e, t);
30856
- return s && D() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
31152
+ return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
30857
31153
  }
30858
31154
  async#B(e, t = {}) {
30859
31155
  let { allowStale: i = this.allowStale, updateAgeOnGet: s = this.updateAgeOnGet, noDeleteOnStaleGet: n = this.noDeleteOnStaleGet, ttl: o = this.ttl, noDisposeOnSet: r = this.noDisposeOnSet, size: h = 0, sizeCalculation: l = this.sizeCalculation, noUpdateTTL: c = this.noUpdateTTL, noDeleteOnFetchRejection: f = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection: g = this.allowStaleOnFetchRejection, ignoreFetchAbort: p = this.ignoreFetchAbort, allowStaleOnFetchAbort: T = this.allowStaleOnFetchAbort, context: w, forceRefresh: y = false, status: a, signal: m } = t;
@@ -30872,7 +31168,7 @@ var L = class u2 {
30872
31168
  }
30873
31169
  let A = this.#p(b);
30874
31170
  if (!y && !A)
30875
- return a && (a.fetch = "hit"), this.#L(b), s && this.#x(b), a && this.#E(a, b), d;
31171
+ return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
30876
31172
  let z5 = this.#P(e, b, _, w), v = z5.__staleWhileFetching !== undefined && i;
30877
31173
  return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z5.__staleWhileFetching : z5.__returned = z5;
30878
31174
  }
@@ -30881,7 +31177,7 @@ var L = class u2 {
30881
31177
  let i = W.hasSubscribers, { status: s = D() ? {} : undefined } = t;
30882
31178
  t.status = s, s && t.context && (s.context = t.context);
30883
31179
  let n = this.#K(e, t);
30884
- return s && D() && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
31180
+ return s && i && (s.trace = true, W.tracePromise(() => n, s).catch(() => {})), n;
30885
31181
  }
30886
31182
  async#K(e, t = {}) {
30887
31183
  let i = await this.#B(e, t);
@@ -30920,7 +31216,7 @@ var L = class u2 {
30920
31216
  return;
30921
31217
  }
30922
31218
  let h = this.#t[r], l = this.#e(h);
30923
- return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#x(r), l ? h.__staleWhileFetching : h);
31219
+ return o && this.#E(o, r), this.#p(r) ? l ? (o && (o.get = "stale-fetching"), i && h.__staleWhileFetching !== undefined ? (o && (o.returnedStale = true), h.__staleWhileFetching) : undefined) : (n || this.#v(e, "expire"), o && (o.get = "stale"), i ? (o && (o.returnedStale = true), h) : undefined) : (o && (o.get = l ? "fetching" : "hit"), this.#L(r), s && this.#D(r), l ? h.__staleWhileFetching : h);
30924
31220
  }
30925
31221
  #$(e, t) {
30926
31222
  this.#c[t] = e, this.#a[e] = t;
@@ -31088,7 +31384,7 @@ function extractStructuredText(root) {
31088
31384
  ]);
31089
31385
  const isText = (node) => node.nodeType === node.TEXT_NODE;
31090
31386
  const isElement = (node) => node.nodeType === node.ELEMENT_NODE;
31091
- const pushText = (value) => {
31387
+ const pushText2 = (value) => {
31092
31388
  const normalized = value.replace(/\s+/g, " ");
31093
31389
  if (!normalized.trim())
31094
31390
  return;
@@ -31114,7 +31410,7 @@ function extractStructuredText(root) {
31114
31410
  };
31115
31411
  const visit = (node) => {
31116
31412
  if (isText(node)) {
31117
- pushText(node.textContent || "");
31413
+ pushText2(node.textContent || "");
31118
31414
  return;
31119
31415
  }
31120
31416
  if (!isElement(node))
@@ -31825,7 +32121,7 @@ async function probeLlmsText(url, timeoutMs, signal, fallbackOrigin) {
31825
32121
  }
31826
32122
 
31827
32123
  // src/tools/smartfetch/cache.ts
31828
- var CACHE = new L({
32124
+ var CACHE = new M({
31829
32125
  maxSize: 50 * 1024 * 1024,
31830
32126
  ttl: 15 * 60 * 1000,
31831
32127
  sizeCalculation: (value) => {
@@ -31886,7 +32182,7 @@ function isInvalidLlmsResult(fetchResult) {
31886
32182
 
31887
32183
  // src/tools/smartfetch/secondary-model.ts
31888
32184
  import { existsSync as existsSync10 } from "node:fs";
31889
- import { readFile as readFile4 } from "node:fs/promises";
32185
+ import { readFile as readFile5 } from "node:fs/promises";
31890
32186
  import path17 from "node:path";
31891
32187
  function parseModelRef(value) {
31892
32188
  if (!value)
@@ -31923,7 +32219,7 @@ async function readOpenCodeConfigFile(configPath) {
31923
32219
  if (!configPath)
31924
32220
  return;
31925
32221
  try {
31926
- const content = await readFile4(configPath, "utf8");
32222
+ const content = await readFile5(configPath, "utf8");
31927
32223
  return JSON.parse(stripJsonComments(content));
31928
32224
  } catch {
31929
32225
  return;
@@ -32572,6 +32868,460 @@ function createWebfetchTool(pluginCtx, options = {}) {
32572
32868
  }
32573
32869
  });
32574
32870
  }
32871
+ // src/tools/subtask/command.ts
32872
+ var COMMAND_NAME5 = "subtask";
32873
+ var SUBTASK_COMMAND_TEMPLATE = `Start a focused subtask worker.
32874
+
32875
+ The user's request below is the full scope for the worker. Do not broaden it.
32876
+ Create a self-contained worker prompt that includes:
32877
+ - the exact objective
32878
+ - relevant context from this conversation
32879
+ - specific files/paths that matter
32880
+ - expected deliverables
32881
+ - validation the worker should run, if applicable
32882
+
32883
+ USER REQUEST:
32884
+ $ARGUMENTS
32885
+
32886
+ Then call the subtask tool:
32887
+ \`subtask(prompt="...", files=["src/foo.ts", "docs/bar.md"])\`
32888
+
32889
+ Only include files that are clearly relevant. If no files are needed, omit files.`;
32890
+ function createSubtaskCommandManager(_ctx, state) {
32891
+ function registerCommand(opencodeConfig) {
32892
+ const configCommand = opencodeConfig.command;
32893
+ if (!configCommand?.[COMMAND_NAME5]) {
32894
+ if (!opencodeConfig.command) {
32895
+ opencodeConfig.command = {};
32896
+ }
32897
+ opencodeConfig.command[COMMAND_NAME5] = {
32898
+ description: "Create a focused subtask prompt for a new session",
32899
+ template: SUBTASK_COMMAND_TEMPLATE
32900
+ };
32901
+ }
32902
+ }
32903
+ return {
32904
+ registerCommand,
32905
+ handleEvent(input) {
32906
+ if (input.event.type === "session.created") {
32907
+ const info = input.event.properties?.info;
32908
+ if (!info?.id || !info.parentID)
32909
+ return;
32910
+ const source = state.sourceFor(info.parentID);
32911
+ if (source)
32912
+ state.markSession(info.id, source);
32913
+ return;
32914
+ }
32915
+ if (input.event.type !== "session.deleted")
32916
+ return;
32917
+ const sessionID = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
32918
+ if (sessionID)
32919
+ state.unmarkSession(sessionID);
32920
+ }
32921
+ };
32922
+ }
32923
+ // src/tools/subtask/files.ts
32924
+ import * as fs12 from "node:fs/promises";
32925
+ import * as path20 from "node:path";
32926
+
32927
+ // src/tools/subtask/vendor.ts
32928
+ import * as fs11 from "node:fs/promises";
32929
+ import * as path19 from "node:path";
32930
+ var DEFAULT_READ_LIMIT = 2000;
32931
+ var MAX_LINE_LENGTH = 2000;
32932
+ var MAX_BYTES = 50 * 1024;
32933
+ var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
32934
+ var MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
32935
+ var SAMPLE_BYTES = 4096;
32936
+ var BINARY_EXTENSIONS = new Set([
32937
+ ".zip",
32938
+ ".tar",
32939
+ ".gz",
32940
+ ".exe",
32941
+ ".dll",
32942
+ ".so",
32943
+ ".class",
32944
+ ".jar",
32945
+ ".war",
32946
+ ".7z",
32947
+ ".doc",
32948
+ ".docx",
32949
+ ".xls",
32950
+ ".xlsx",
32951
+ ".ppt",
32952
+ ".pptx",
32953
+ ".odt",
32954
+ ".ods",
32955
+ ".odp",
32956
+ ".bin",
32957
+ ".dat",
32958
+ ".obj",
32959
+ ".o",
32960
+ ".a",
32961
+ ".lib",
32962
+ ".wasm",
32963
+ ".pyc",
32964
+ ".pyo"
32965
+ ]);
32966
+ async function isBinaryFile(filepath) {
32967
+ const ext = path19.extname(filepath).toLowerCase();
32968
+ if (BINARY_EXTENSIONS.has(ext)) {
32969
+ return true;
32970
+ }
32971
+ try {
32972
+ const file = await fs11.open(filepath, "r");
32973
+ try {
32974
+ const buffer = Buffer.alloc(SAMPLE_BYTES);
32975
+ const result = await file.read(buffer, 0, SAMPLE_BYTES, 0);
32976
+ if (result.bytesRead === 0)
32977
+ return false;
32978
+ const bytes = buffer.subarray(0, result.bytesRead);
32979
+ let nonPrintableCount = 0;
32980
+ for (let i = 0;i < bytes.length; i++) {
32981
+ const byte = bytes[i];
32982
+ if (byte === undefined)
32983
+ continue;
32984
+ if (byte === 0)
32985
+ return true;
32986
+ if (byte < 9 || byte > 13 && byte < 32) {
32987
+ nonPrintableCount++;
32988
+ }
32989
+ }
32990
+ return nonPrintableCount / bytes.length > 0.3;
32991
+ } finally {
32992
+ await file.close();
32993
+ }
32994
+ } catch {
32995
+ return false;
32996
+ }
32997
+ }
32998
+ function formatFileContent(_filepath, content) {
32999
+ const cappedContent = Buffer.byteLength(content, "utf8") > MAX_BYTES;
33000
+ const contentToFormat = cappedContent ? content.slice(0, MAX_BYTES) : content;
33001
+ const lines = contentToFormat.split(`
33002
+ `);
33003
+ const limit = DEFAULT_READ_LIMIT;
33004
+ const offset = 0;
33005
+ const raw = lines.slice(offset, offset + limit).map((line) => {
33006
+ return line.length > MAX_LINE_LENGTH ? `${line.substring(0, MAX_LINE_LENGTH)}${MAX_LINE_SUFFIX}` : line;
33007
+ });
33008
+ const formatted = raw.map((line, index) => {
33009
+ return `${index + offset + 1}: ${line}`;
33010
+ });
33011
+ let output = [
33012
+ `<path>${_filepath}</path>`,
33013
+ "<type>file</type>",
33014
+ `<content>
33015
+ `
33016
+ ].join(`
33017
+ `);
33018
+ output += formatted.join(`
33019
+ `);
33020
+ const totalLines = lines.length;
33021
+ const lastReadLine = offset + formatted.length;
33022
+ const hasMoreLines = totalLines > lastReadLine;
33023
+ if (cappedContent) {
33024
+ output += `
33025
+
33026
+ (Output capped at ${MAX_BYTES_LABEL}. Showing lines 1-${lastReadLine}. Use offset=${lastReadLine + 1} to continue.)`;
33027
+ } else if (hasMoreLines) {
33028
+ output += `
33029
+
33030
+ (Showing lines 1-${lastReadLine} of ${totalLines}. Use offset=${lastReadLine + 1} to continue.)`;
33031
+ } else {
33032
+ output += `
33033
+
33034
+ (End of file - total ${totalLines} lines)`;
33035
+ }
33036
+ output += `
33037
+ </content>`;
33038
+ return output;
33039
+ }
33040
+
33041
+ // src/tools/subtask/files.ts
33042
+ var FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g;
33043
+ var TRAILING_PATH_PUNCTUATION = /[!?:;]+$/;
33044
+ function cleanFileReference(ref) {
33045
+ return ref.replace(/^@/, "").replace(TRAILING_PATH_PUNCTUATION, "");
33046
+ }
33047
+ function parseFileReferences(text) {
33048
+ const fileRefs = new Set;
33049
+ for (const match of text.matchAll(FILE_REGEX)) {
33050
+ if (match[1]) {
33051
+ fileRefs.add(cleanFileReference(match[1]));
33052
+ }
33053
+ }
33054
+ return fileRefs;
33055
+ }
33056
+ async function buildSyntheticFileParts(directory, refs) {
33057
+ const parts = [];
33058
+ const realDirectory = await fs12.realpath(directory);
33059
+ for (const ref of refs) {
33060
+ const filepath = path20.resolve(directory, ref);
33061
+ const relative3 = path20.relative(directory, filepath);
33062
+ if (relative3.startsWith("..") || path20.isAbsolute(relative3))
33063
+ continue;
33064
+ try {
33065
+ const realFilepath = await fs12.realpath(filepath);
33066
+ const realRelative = path20.relative(realDirectory, realFilepath);
33067
+ if (realRelative.startsWith("..") || path20.isAbsolute(realRelative)) {
33068
+ continue;
33069
+ }
33070
+ const stats = await fs12.stat(realFilepath);
33071
+ if (!stats.isFile())
33072
+ continue;
33073
+ if (await isBinaryFile(realFilepath))
33074
+ continue;
33075
+ const content = await fs12.readFile(realFilepath, "utf-8");
33076
+ parts.push({
33077
+ type: "text",
33078
+ synthetic: true,
33079
+ text: `Called the Read tool with the following input: ${JSON.stringify({ filePath: realFilepath })}`
33080
+ });
33081
+ parts.push({
33082
+ type: "text",
33083
+ synthetic: true,
33084
+ text: formatFileContent(realFilepath, content)
33085
+ });
33086
+ } catch {}
33087
+ }
33088
+ return parts;
33089
+ }
33090
+ // src/tools/subtask/state.ts
33091
+ function createSubtaskState() {
33092
+ const sourceBySession = new Map;
33093
+ return {
33094
+ markSession(sessionID, sourceSessionID) {
33095
+ sourceBySession.set(sessionID, sourceSessionID);
33096
+ },
33097
+ unmarkSession(sessionID) {
33098
+ sourceBySession.delete(sessionID);
33099
+ },
33100
+ isSubtaskSession(sessionID) {
33101
+ return sourceBySession.has(sessionID);
33102
+ },
33103
+ sourceFor(sessionID) {
33104
+ return sourceBySession.get(sessionID);
33105
+ }
33106
+ };
33107
+ }
33108
+ // src/tools/subtask/tools.ts
33109
+ import { tool as tool5 } from "@opencode-ai/plugin";
33110
+ var SUBTASK_TIMEOUT_MS = 5 * 60 * 1000;
33111
+ var SUBTASK_SUMMARY_TAG_REGEX = /<\/?subtask_summary>/g;
33112
+ function normalizeSubtaskSummary(text) {
33113
+ return text.replace(SUBTASK_SUMMARY_TAG_REGEX, "").trim();
33114
+ }
33115
+ function getAbortSignal(context) {
33116
+ if (!context || typeof context !== "object" || !("abort" in context)) {
33117
+ return;
33118
+ }
33119
+ const signal = context.abort;
33120
+ return signal && typeof signal === "object" && "addEventListener" in signal && "removeEventListener" in signal && "aborted" in signal ? signal : undefined;
33121
+ }
33122
+ function createSubtaskTool(ctx, state, depthTracker) {
33123
+ const client = ctx.client;
33124
+ return tool5({
33125
+ description: "Run a child worker session and return its completion summary to the caller",
33126
+ args: {
33127
+ prompt: tool5.schema.string().describe("The generated subtask prompt"),
33128
+ files: tool5.schema.array(tool5.schema.string()).optional().describe("Array of file paths to load into the new session's context")
33129
+ },
33130
+ async execute(args, context) {
33131
+ const directory = context && typeof context === "object" && "directory" in context && typeof context.directory === "string" ? context.directory : ctx.directory;
33132
+ const sessionID = context && typeof context === "object" && "sessionID" in context ? context.sessionID : "unknown";
33133
+ const abortSignal = getAbortSignal(context);
33134
+ if (state.isSubtaskSession(sessionID)) {
33135
+ return "Nested subtask is disabled: this session is already a subtask worker. Finish this worker and return its summary to the parent session instead.";
33136
+ }
33137
+ if (sessionID !== "unknown" && depthTracker && depthTracker.getDepth(sessionID) + 1 > depthTracker.maxDepth) {
33138
+ return `Subtask worker blocked: max subagent depth ${depthTracker.maxDepth} would be exceeded.`;
33139
+ }
33140
+ const sessionReference = `You are a subtask worker spawned by parent session ${sessionID}.
33141
+
33142
+ Your job is bounded: complete only the task below. Do not expand scope.
33143
+ If needed context is missing, use read_session to inspect the parent session.
33144
+ Do not spawn another subtask.`;
33145
+ const files = new Set([
33146
+ ...parseFileReferences(args.prompt),
33147
+ ...(args.files ?? []).map(cleanFileReference)
33148
+ ]);
33149
+ const fileRefs = files.size > 0 ? [...files].map((f) => `@${f}`).join(" ") : "";
33150
+ const fullPrompt = fileRefs ? `${sessionReference}
33151
+
33152
+ TASK:
33153
+ ${args.prompt}
33154
+
33155
+ FILES PROVIDED:
33156
+ ${fileRefs}` : `${sessionReference}
33157
+
33158
+ TASK:
33159
+ ${args.prompt}`;
33160
+ let childSessionID;
33161
+ try {
33162
+ const session2 = await client.session.create({
33163
+ responseStyle: "data",
33164
+ throwOnError: true,
33165
+ query: { directory },
33166
+ body: {
33167
+ parentID: sessionID === "unknown" ? undefined : sessionID,
33168
+ title: `Subtask worker from ${sessionID}`
33169
+ }
33170
+ });
33171
+ childSessionID = session2?.data?.id ?? session2?.id;
33172
+ if (!childSessionID) {
33173
+ throw new Error("Subtask worker session did not return an id");
33174
+ }
33175
+ if (sessionID !== "unknown" && depthTracker) {
33176
+ const registered = depthTracker.registerChild(sessionID, childSessionID);
33177
+ if (!registered) {
33178
+ throw new Error("Subtask worker blocked: max subagent depth exceeded");
33179
+ }
33180
+ }
33181
+ state.markSession(childSessionID, sessionID);
33182
+ await promptWithTimeout(client, {
33183
+ responseStyle: "data",
33184
+ throwOnError: true,
33185
+ query: { directory },
33186
+ path: { id: childSessionID },
33187
+ body: {
33188
+ agent: "orchestrator",
33189
+ parts: [
33190
+ {
33191
+ type: "text",
33192
+ text: `${fullPrompt}
33193
+
33194
+ Instructions:
33195
+ 1. Understand the task and relevant file context.
33196
+ 2. Make only necessary changes.
33197
+ 3. Run the most relevant validation checks when practical.
33198
+ 4. Stop when the requested task is done.
33199
+
33200
+ Return your final response in this format:
33201
+
33202
+ <subtask_summary>
33203
+ Status: completed | blocked | partial
33204
+
33205
+ What changed:
33206
+ - ...
33207
+
33208
+ Files touched:
33209
+ - ...
33210
+
33211
+ Validation:
33212
+ - ...
33213
+
33214
+ Risks / follow-up:
33215
+ - ...
33216
+ </subtask_summary>`
33217
+ },
33218
+ ...await buildSyntheticFileParts(directory, files)
33219
+ ]
33220
+ }
33221
+ }, SUBTASK_TIMEOUT_MS, abortSignal);
33222
+ const extraction = await extractSessionResult(client, childSessionID, {
33223
+ directory,
33224
+ includeReasoning: false
33225
+ });
33226
+ if (extraction.empty) {
33227
+ throw new Error("Subtask worker returned no summary");
33228
+ }
33229
+ const summary = normalizeSubtaskSummary(extraction.text);
33230
+ return [
33231
+ `task_id: ${childSessionID}`,
33232
+ "",
33233
+ "<subtask_summary>",
33234
+ summary,
33235
+ "</subtask_summary>"
33236
+ ].join(`
33237
+ `);
33238
+ } finally {
33239
+ if (childSessionID) {
33240
+ try {
33241
+ await client.session.abort({
33242
+ path: { id: childSessionID },
33243
+ query: { directory }
33244
+ });
33245
+ state.unmarkSession(childSessionID);
33246
+ } catch {}
33247
+ }
33248
+ }
33249
+ }
33250
+ });
33251
+ }
33252
+ function formatTranscript(messages, limit) {
33253
+ const lines = [];
33254
+ for (const msg of messages) {
33255
+ const role = msg.info?.role;
33256
+ const parts = msg.parts;
33257
+ if (role === "user") {
33258
+ lines.push("## User");
33259
+ for (const part of parts) {
33260
+ if (part.type === "text" && !part.ignored && typeof part.text === "string") {
33261
+ lines.push(part.text);
33262
+ }
33263
+ if (part.type === "file") {
33264
+ lines.push(`[Attached: ${part.filename || "file"}]`);
33265
+ }
33266
+ }
33267
+ lines.push("");
33268
+ }
33269
+ if (role === "assistant") {
33270
+ lines.push("## Assistant");
33271
+ for (const part of parts) {
33272
+ if (part.type === "text" && typeof part.text === "string") {
33273
+ lines.push(part.text);
33274
+ }
33275
+ if (part.type === "tool" && part.state?.status === "completed" && part.tool) {
33276
+ lines.push(`[Tool: ${part.tool}] ${part.state.title ?? ""}`);
33277
+ }
33278
+ }
33279
+ lines.push("");
33280
+ }
33281
+ }
33282
+ const output = lines.join(`
33283
+ `).trim();
33284
+ if (messages.length >= (limit ?? 100)) {
33285
+ return output + `
33286
+
33287
+ (Showing ${messages.length} most recent messages. Use a higher 'limit' to see more.)`;
33288
+ }
33289
+ return `${output}
33290
+
33291
+ (End of session - ${messages.length} messages)`;
33292
+ }
33293
+ function createReadSessionTool(client, state) {
33294
+ return tool5({
33295
+ description: "Read the conversation transcript from a previous session. Use this when you need specific information from the source session that wasn't included in the subtask summary.",
33296
+ args: {
33297
+ sessionID: tool5.schema.string().describe("The full session ID (e.g., sess_01jxyz...)"),
33298
+ limit: tool5.schema.number().optional().describe("Maximum number of messages to read (defaults to 100, max 500)")
33299
+ },
33300
+ async execute(args, context) {
33301
+ const limit = Math.min(args.limit ?? 100, 500);
33302
+ const directory = context && typeof context === "object" && "directory" in context && typeof context.directory === "string" ? context.directory : undefined;
33303
+ const callerSessionID = context && typeof context === "object" && "sessionID" in context ? context.sessionID : undefined;
33304
+ if (!callerSessionID || !state.isSubtaskSession(callerSessionID)) {
33305
+ return "read_session is only available from subtask worker sessions.";
33306
+ }
33307
+ if (state.sourceFor(callerSessionID) !== args.sessionID) {
33308
+ return "read_session can only read the source session for this subtask worker.";
33309
+ }
33310
+ try {
33311
+ const response = await client.session.messages({
33312
+ path: { id: args.sessionID },
33313
+ query: { limit, ...directory ? { directory } : {} }
33314
+ });
33315
+ if (!response.data || response.data.length === 0) {
33316
+ return "Session has no messages or does not exist.";
33317
+ }
33318
+ return formatTranscript(response.data, limit);
33319
+ } catch (error) {
33320
+ return `Could not read session ${args.sessionID}: ${error instanceof Error ? error.message : "Unknown error"}`;
33321
+ }
33322
+ }
33323
+ });
33324
+ }
32575
33325
  // src/utils/subagent-depth.ts
32576
33326
  class SubagentDepthTracker {
32577
33327
  depthBySession = new Map;
@@ -32684,6 +33434,7 @@ var OhMyOpenCodeLite = async (ctx) => {
32684
33434
  let jsonErrorRecoveryHook;
32685
33435
  let foregroundFallback;
32686
33436
  let todoContinuationHook;
33437
+ let sessionGoalHook;
32687
33438
  let taskSessionManagerHook;
32688
33439
  let interviewManager;
32689
33440
  let presetManager;
@@ -32691,6 +33442,8 @@ var OhMyOpenCodeLite = async (ctx) => {
32691
33442
  let councilTools;
32692
33443
  let webfetch;
32693
33444
  let rewriteDisplayNameMentions;
33445
+ let subtaskCommandManager;
33446
+ let subtaskState;
32694
33447
  let toolCount = 0;
32695
33448
  try {
32696
33449
  config = loadPluginConfig(ctx.directory);
@@ -32774,6 +33527,9 @@ var OhMyOpenCodeLite = async (ctx) => {
32774
33527
  autoEnable: config.todoContinuation?.autoEnable ?? false,
32775
33528
  autoEnableThreshold: config.todoContinuation?.autoEnableThreshold ?? 4
32776
33529
  });
33530
+ sessionGoalHook = createSessionGoalHook(ctx, config, {
33531
+ getAgentName: (sessionID) => sessionAgentMap.get(sessionID)
33532
+ });
32777
33533
  taskSessionManagerHook = createTaskSessionManagerHook(ctx, {
32778
33534
  maxSessionsPerAgent: config.sessionManager?.maxSessionsPerAgent ?? 2,
32779
33535
  readContextMinLines: config.sessionManager?.readContextMinLines ?? 10,
@@ -32783,7 +33539,9 @@ var OhMyOpenCodeLite = async (ctx) => {
32783
33539
  interviewManager = createInterviewManager(ctx, config);
32784
33540
  presetManager = createPresetManager(ctx, config);
32785
33541
  divoomManager = createDivoomManager(config.divoom);
32786
- toolCount = Object.keys(councilTools).length + Object.keys(todoContinuationHook.tool).length + 1 + 2;
33542
+ subtaskState = createSubtaskState();
33543
+ subtaskCommandManager = createSubtaskCommandManager(ctx, subtaskState);
33544
+ toolCount = Object.keys(councilTools).length + Object.keys(todoContinuationHook.tool).length + 1 + 2 + 2;
32787
33545
  } catch (err) {
32788
33546
  log("[plugin] FATAL: init failed", String(err));
32789
33547
  await appLog(ctx, "error", `INIT FAILED: ${String(err)}. Report at github.com/alvinunreal/oh-my-opencode-slim/issues/310`);
@@ -32828,7 +33586,9 @@ var OhMyOpenCodeLite = async (ctx) => {
32828
33586
  webfetch,
32829
33587
  ...todoContinuationHook.tool,
32830
33588
  ast_grep_search,
32831
- ast_grep_replace
33589
+ ast_grep_replace,
33590
+ subtask: createSubtaskTool(ctx, subtaskState, depthTracker),
33591
+ read_session: createReadSessionTool(ctx.client, subtaskState)
32832
33592
  },
32833
33593
  mcp: mcps,
32834
33594
  config: async (opencodeConfig) => {
@@ -33024,7 +33784,9 @@ var OhMyOpenCodeLite = async (ctx) => {
33024
33784
  };
33025
33785
  }
33026
33786
  interviewManager.registerCommand(opencodeConfig);
33787
+ sessionGoalHook.registerCommand(opencodeConfig);
33027
33788
  presetManager.registerCommand(opencodeConfig);
33789
+ subtaskCommandManager.registerCommand(opencodeConfig);
33028
33790
  },
33029
33791
  event: async (input) => {
33030
33792
  const event = input.event;
@@ -33044,14 +33806,16 @@ var OhMyOpenCodeLite = async (ctx) => {
33044
33806
  depthTracker.registerChild(parentSessionId, childSessionId);
33045
33807
  }
33046
33808
  }
33047
- await foregroundFallback.handleEvent(input.event);
33048
- await todoContinuationHook.handleEvent(input);
33049
- await autoUpdateChecker.event(input);
33050
33809
  await multiplexerSessionManager.onSessionCreated(event);
33051
33810
  await multiplexerSessionManager.onSessionStatus(event);
33052
33811
  await multiplexerSessionManager.onSessionDeleted(event);
33812
+ await foregroundFallback.handleEvent(input.event);
33813
+ await todoContinuationHook.handleEvent(input);
33814
+ sessionGoalHook.handleEvent(input);
33815
+ await autoUpdateChecker.event(input);
33053
33816
  await interviewManager.handleEvent(input);
33054
33817
  await taskSessionManagerHook.event(input);
33818
+ subtaskCommandManager.handleEvent(input);
33055
33819
  if (event.type === "permission.asked" || event.type === "question.asked") {
33056
33820
  const props = event.properties;
33057
33821
  divoomManager.onUserInputRequired({
@@ -33109,6 +33873,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33109
33873
  await todoContinuationHook.handleCommandExecuteBefore(input, output);
33110
33874
  await interviewManager.handleCommandExecuteBefore(input, output);
33111
33875
  await presetManager.handleCommandExecuteBefore(input, output);
33876
+ await sessionGoalHook.handleCommandExecuteBefore(input, output);
33112
33877
  },
33113
33878
  "chat.headers": chatHeadersHook["chat.headers"],
33114
33879
  "chat.message": async (input, output) => {
@@ -33137,6 +33902,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33137
33902
  ${output.system[0]}` : "");
33138
33903
  }
33139
33904
  }
33905
+ sessionGoalHook.handleSystemTransform(input, output);
33140
33906
  collapseSystemInPlace(output.system);
33141
33907
  },
33142
33908
  "experimental.chat.messages.transform": async (input, output) => {