micode 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,17 @@
1
1
  // @bun
2
2
  var __defProp = Object.defineProperty;
3
3
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
4
+ var __returnValue = (v) => v;
5
+ function __exportSetter(name, newValue) {
6
+ this[name] = __returnValue.bind(null, newValue);
7
+ }
4
8
  var __export = (target, all) => {
5
9
  for (var name in all)
6
10
  __defProp(target, name, {
7
11
  get: all[name],
8
12
  enumerable: true,
9
13
  configurable: true,
10
- set: (newValue) => all[name] = () => newValue
14
+ set: __exportSetter.bind(all, name)
11
15
  });
12
16
  };
13
17
  var __require = import.meta.require;
@@ -72,7 +76,7 @@ var require_visit = __commonJS((exports) => {
72
76
  var BREAK = Symbol("break visit");
73
77
  var SKIP = Symbol("skip children");
74
78
  var REMOVE = Symbol("remove node");
75
- function visit(node, visitor) {
79
+ function visit2(node, visitor) {
76
80
  const visitor_ = initVisitor(visitor);
77
81
  if (identity.isDocument(node)) {
78
82
  const cd = visit_(null, node.contents, visitor_, Object.freeze([node]));
@@ -81,9 +85,9 @@ var require_visit = __commonJS((exports) => {
81
85
  } else
82
86
  visit_(null, node, visitor_, Object.freeze([]));
83
87
  }
84
- visit.BREAK = BREAK;
85
- visit.SKIP = SKIP;
86
- visit.REMOVE = REMOVE;
88
+ visit2.BREAK = BREAK;
89
+ visit2.SKIP = SKIP;
90
+ visit2.REMOVE = REMOVE;
87
91
  function visit_(key, node, visitor, path) {
88
92
  const ctrl = callVisitor(key, node, visitor, path);
89
93
  if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
@@ -217,14 +221,14 @@ var require_visit = __commonJS((exports) => {
217
221
  throw new Error(`Cannot replace node with ${pt} parent`);
218
222
  }
219
223
  }
220
- exports.visit = visit;
224
+ exports.visit = visit2;
221
225
  exports.visitAsync = visitAsync;
222
226
  });
223
227
 
224
228
  // node_modules/yaml/dist/doc/directives.js
225
229
  var require_directives = __commonJS((exports) => {
226
230
  var identity = require_identity();
227
- var visit = require_visit();
231
+ var visit2 = require_visit();
228
232
  var escapeChars = {
229
233
  "!": "%21",
230
234
  ",": "%2C",
@@ -351,7 +355,7 @@ var require_directives = __commonJS((exports) => {
351
355
  let tagNames;
352
356
  if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) {
353
357
  const tags = {};
354
- visit.visit(doc.contents, (_key, node) => {
358
+ visit2.visit(doc.contents, (_key, node) => {
355
359
  if (identity.isNode(node) && node.tag)
356
360
  tags[node.tag] = true;
357
361
  });
@@ -376,7 +380,7 @@ var require_directives = __commonJS((exports) => {
376
380
  // node_modules/yaml/dist/doc/anchors.js
377
381
  var require_anchors = __commonJS((exports) => {
378
382
  var identity = require_identity();
379
- var visit = require_visit();
383
+ var visit2 = require_visit();
380
384
  function anchorIsValid(anchor) {
381
385
  if (/[\x00-\x19\s,[\]{}]/.test(anchor)) {
382
386
  const sa = JSON.stringify(anchor);
@@ -387,7 +391,7 @@ var require_anchors = __commonJS((exports) => {
387
391
  }
388
392
  function anchorNames(root) {
389
393
  const anchors = new Set;
390
- visit.visit(root, {
394
+ visit2.visit(root, {
391
395
  Value(_key, node) {
392
396
  if (node.anchor)
393
397
  anchors.add(node.anchor);
@@ -549,7 +553,7 @@ var require_Node = __commonJS((exports) => {
549
553
  // node_modules/yaml/dist/nodes/Alias.js
550
554
  var require_Alias = __commonJS((exports) => {
551
555
  var anchors = require_anchors();
552
- var visit = require_visit();
556
+ var visit2 = require_visit();
553
557
  var identity = require_identity();
554
558
  var Node = require_Node();
555
559
  var toJS = require_toJS();
@@ -570,7 +574,7 @@ var require_Alias = __commonJS((exports) => {
570
574
  nodes = ctx.aliasResolveCache;
571
575
  } else {
572
576
  nodes = [];
573
- visit.visit(doc, {
577
+ visit2.visit(doc, {
574
578
  Node: (_key, node) => {
575
579
  if (identity.isAlias(node) || identity.hasAnchor(node))
576
580
  nodes.push(node);
@@ -2206,14 +2210,14 @@ var require_bool = __commonJS((exports) => {
2206
2210
 
2207
2211
  // node_modules/yaml/dist/stringify/stringifyNumber.js
2208
2212
  var require_stringifyNumber = __commonJS((exports) => {
2209
- function stringifyNumber({ format, minFractionDigits, tag, value }) {
2213
+ function stringifyNumber({ format: format2, minFractionDigits, tag, value }) {
2210
2214
  if (typeof value === "bigint")
2211
2215
  return String(value);
2212
2216
  const num = typeof value === "number" ? value : Number(value);
2213
2217
  if (!isFinite(num))
2214
2218
  return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
2215
2219
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
2216
- if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^\d/.test(n)) {
2220
+ if (!format2 && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^\d/.test(n)) {
2217
2221
  let i = n.indexOf(".");
2218
2222
  if (i < 0) {
2219
2223
  i = n.length;
@@ -5202,15 +5206,15 @@ var require_cst_visit = __commonJS((exports) => {
5202
5206
  var BREAK = Symbol("break visit");
5203
5207
  var SKIP = Symbol("skip children");
5204
5208
  var REMOVE = Symbol("remove item");
5205
- function visit(cst, visitor) {
5209
+ function visit2(cst, visitor) {
5206
5210
  if ("type" in cst && cst.type === "document")
5207
5211
  cst = { start: cst.start, value: cst.value };
5208
5212
  _visit(Object.freeze([]), cst, visitor);
5209
5213
  }
5210
- visit.BREAK = BREAK;
5211
- visit.SKIP = SKIP;
5212
- visit.REMOVE = REMOVE;
5213
- visit.itemAtPath = (cst, path) => {
5214
+ visit2.BREAK = BREAK;
5215
+ visit2.SKIP = SKIP;
5216
+ visit2.REMOVE = REMOVE;
5217
+ visit2.itemAtPath = (cst, path) => {
5214
5218
  let item = cst;
5215
5219
  for (const [field, index] of path) {
5216
5220
  const tok = item?.[field];
@@ -5221,8 +5225,8 @@ var require_cst_visit = __commonJS((exports) => {
5221
5225
  }
5222
5226
  return item;
5223
5227
  };
5224
- visit.parentCollection = (cst, path) => {
5225
- const parent = visit.itemAtPath(cst, path.slice(0, -1));
5228
+ visit2.parentCollection = (cst, path) => {
5229
+ const parent = visit2.itemAtPath(cst, path.slice(0, -1));
5226
5230
  const field = path[path.length - 1][0];
5227
5231
  const coll = parent?.[field];
5228
5232
  if (coll && "items" in coll)
@@ -5253,7 +5257,7 @@ var require_cst_visit = __commonJS((exports) => {
5253
5257
  }
5254
5258
  return typeof ctrl === "function" ? ctrl(item, path) : ctrl;
5255
5259
  }
5256
- exports.visit = visit;
5260
+ exports.visit = visit2;
5257
5261
  });
5258
5262
 
5259
5263
  // node_modules/yaml/dist/parse/cst.js
@@ -6867,7 +6871,7 @@ var require_public_api = __commonJS((exports) => {
6867
6871
  }
6868
6872
  return doc;
6869
6873
  }
6870
- function parse2(src, reviver, options) {
6874
+ function parse4(src, reviver, options) {
6871
6875
  let _reviver = undefined;
6872
6876
  if (typeof reviver === "function") {
6873
6877
  _reviver = reviver;
@@ -6908,12 +6912,85 @@ var require_public_api = __commonJS((exports) => {
6908
6912
  return value.toString(options);
6909
6913
  return new Document.Document(value, _replacer, options).toString(options);
6910
6914
  }
6911
- exports.parse = parse2;
6915
+ exports.parse = parse4;
6912
6916
  exports.parseAllDocuments = parseAllDocuments;
6913
6917
  exports.parseDocument = parseDocument;
6914
6918
  exports.stringify = stringify;
6915
6919
  });
6916
6920
 
6921
+ // src/utils/config.ts
6922
+ var config = {
6923
+ compaction: {
6924
+ threshold: 0.7,
6925
+ cooldownMs: 120000,
6926
+ timeoutMs: 120000
6927
+ },
6928
+ contextWindow: {
6929
+ warningThreshold: 0.7,
6930
+ criticalThreshold: 0.85,
6931
+ warningCooldownMs: 120000
6932
+ },
6933
+ tokens: {
6934
+ charsPerToken: 4,
6935
+ defaultContextLimit: 200000,
6936
+ defaultMaxOutputTokens: 50000,
6937
+ safetyMargin: 0.5,
6938
+ preserveHeaderLines: 3
6939
+ },
6940
+ paths: {
6941
+ ledgerDir: "thoughts/ledgers",
6942
+ ledgerPrefix: "CONTINUITY_",
6943
+ rootContextFiles: ["README.md", "ARCHITECTURE.md", "CODE_STYLE.md"],
6944
+ dirContextFiles: ["README.md"],
6945
+ planPattern: /thoughts\/shared\/plans\/.*\.md$/,
6946
+ ledgerPattern: /thoughts\/ledgers\/CONTINUITY_.*\.md$/,
6947
+ mindmodelDir: ".mindmodel",
6948
+ mindmodelManifest: "manifest.yaml",
6949
+ mindmodelSystem: "system.md"
6950
+ },
6951
+ timeouts: {
6952
+ btcaMs: 120000,
6953
+ toastSuccessMs: 3000,
6954
+ toastWarningMs: 4000,
6955
+ toastErrorMs: 5000
6956
+ },
6957
+ limits: {
6958
+ largeFileBytes: 100 * 1024,
6959
+ maxLinesNoExtract: 200,
6960
+ ptyMaxBufferLines: 50000,
6961
+ ptyDefaultReadLimit: 500,
6962
+ ptyMaxLineLength: 2000,
6963
+ astGrepMaxMatches: 100,
6964
+ contextCacheTtlMs: 30000,
6965
+ contextCacheMaxSize: 100
6966
+ },
6967
+ octto: {
6968
+ answerTimeoutMs: 5 * 60 * 1000,
6969
+ reviewTimeoutMs: 10 * 60 * 1000,
6970
+ maxIterations: 50,
6971
+ maxQuestions: 15,
6972
+ stateDir: "thoughts/brainstorms",
6973
+ bindAddress: "127.0.0.1",
6974
+ allowRemoteBind: false
6975
+ },
6976
+ model: {
6977
+ default: "openai/gpt-5.2-codex"
6978
+ },
6979
+ mindmodel: {
6980
+ overrideLogFile: "overrides.log",
6981
+ reviewMaxRetries: 1,
6982
+ reviewEnabled: true,
6983
+ categoryGroups: ["stack", "architecture", "patterns", "style", "components", "domain", "ops"]
6984
+ },
6985
+ fetch: {
6986
+ warnThreshold: 3,
6987
+ maxCallsPerResource: 5,
6988
+ cacheTtlMs: 300000,
6989
+ cacheMaxEntries: 50
6990
+ }
6991
+ };
6992
+ var DEFAULT_MODEL = config.model.default;
6993
+
6917
6994
  // src/agents/artifact-searcher.ts
6918
6995
  var artifactSearcherAgent = {
6919
6996
  description: "Searches past handoffs, plans, and ledgers for relevant precedent",
@@ -7262,7 +7339,7 @@ The redesigned artifact system treats artifacts as first\u2011class records stor
7262
7339
 
7263
7340
  <phase name="finalizing" trigger="after presenting design">
7264
7341
  <action>Write validated design to thoughts/shared/designs/YYYY-MM-DD-{topic}-design.md</action>
7265
- <action>Commit the design document to git</action>
7342
+ <action>Commit the design document to git (if git add fails because the file is gitignored, skip the commit \u2014 NEVER force-add ignored files)</action>
7266
7343
  <action>IMMEDIATELY spawn planner - do NOT ask "Ready for planner?"</action>
7267
7344
  <spawn>
7268
7345
  Task(
@@ -7668,6 +7745,7 @@ Not everything needs brainstorm \u2192 plan \u2192 execute.
7668
7745
  <rule>Commit message format: type(scope): description</rule>
7669
7746
  <rule>Types: feat, fix, refactor, docs, test, chore</rule>
7670
7747
  <rule>Reference plan file in commit body</rule>
7748
+ <rule>NEVER use git add -f or --force. If a file is gitignored, respect it and skip it.</rule>
7671
7749
  </phase>
7672
7750
 
7673
7751
  <phase name="ledger" trigger="context getting full or session ending">
@@ -7789,7 +7867,7 @@ var primaryAgent = {
7789
7867
  temperature: 0.2,
7790
7868
  thinking: {
7791
7869
  type: "enabled",
7792
- budgetTokens: 32000
7870
+ budgetTokens: 64000
7793
7871
  },
7794
7872
  maxTokens: 64000,
7795
7873
  tools: {
@@ -10476,221 +10554,1062 @@ Code: \`for (let i = 0; i <= arr.length - 1; i++)\`
10476
10554
 
10477
10555
  // src/agents/index.ts
10478
10556
  var agents = {
10479
- [PRIMARY_AGENT_NAME]: { ...primaryAgent, model: "openai/gpt-5.2-codex" },
10480
- brainstormer: { ...brainstormerAgent, model: "openai/gpt-5.2-codex" },
10481
- bootstrapper: { ...bootstrapperAgent, model: "openai/gpt-5.2-codex" },
10482
- "codebase-locator": { ...codebaseLocatorAgent, model: "openai/gpt-5.2-codex" },
10483
- "codebase-analyzer": { ...codebaseAnalyzerAgent, model: "openai/gpt-5.2-codex" },
10484
- "pattern-finder": { ...patternFinderAgent, model: "openai/gpt-5.2-codex" },
10485
- planner: { ...plannerAgent, model: "openai/gpt-5.2-codex" },
10486
- implementer: { ...implementerAgent, model: "openai/gpt-5.2-codex" },
10487
- reviewer: { ...reviewerAgent, model: "openai/gpt-5.2-codex" },
10488
- executor: { ...executorAgent, model: "openai/gpt-5.2-codex" },
10489
- "ledger-creator": { ...ledgerCreatorAgent, model: "openai/gpt-5.2-codex" },
10490
- "artifact-searcher": { ...artifactSearcherAgent, model: "openai/gpt-5.2-codex" },
10491
- "project-initializer": { ...projectInitializerAgent, model: "openai/gpt-5.2-codex" },
10492
- octto: { ...octtoAgent, model: "openai/gpt-5.2-codex" },
10493
- probe: { ...probeAgent, model: "openai/gpt-5.2-codex" },
10494
- "mm-stack-detector": { ...stackDetectorAgent, model: "openai/gpt-5.2-codex" },
10495
- "mm-pattern-discoverer": { ...mindmodelPatternDiscovererAgent, model: "openai/gpt-5.2-codex" },
10496
- "mm-example-extractor": { ...exampleExtractorAgent, model: "openai/gpt-5.2-codex" },
10497
- "mm-orchestrator": { ...mindmodelOrchestratorAgent, model: "openai/gpt-5.2-codex" },
10498
- "mm-dependency-mapper": { ...dependencyMapperAgent, model: "openai/gpt-5.2-codex" },
10499
- "mm-convention-extractor": { ...conventionExtractorAgent, model: "openai/gpt-5.2-codex" },
10500
- "mm-domain-extractor": { ...domainExtractorAgent, model: "openai/gpt-5.2-codex" },
10501
- "mm-code-clusterer": { ...codeClustererAgent, model: "openai/gpt-5.2-codex" },
10502
- "mm-anti-pattern-detector": { ...antiPatternDetectorAgent, model: "openai/gpt-5.2-codex" },
10503
- "mm-constraint-writer": { ...constraintWriterAgent, model: "openai/gpt-5.2-codex" },
10504
- "mm-constraint-reviewer": { ...constraintReviewerAgent, model: "openai/gpt-5.2-codex" }
10557
+ [PRIMARY_AGENT_NAME]: { ...primaryAgent, model: DEFAULT_MODEL },
10558
+ brainstormer: { ...brainstormerAgent, model: DEFAULT_MODEL },
10559
+ bootstrapper: { ...bootstrapperAgent, model: DEFAULT_MODEL },
10560
+ "codebase-locator": { ...codebaseLocatorAgent, model: DEFAULT_MODEL },
10561
+ "codebase-analyzer": { ...codebaseAnalyzerAgent, model: DEFAULT_MODEL },
10562
+ "pattern-finder": { ...patternFinderAgent, model: DEFAULT_MODEL },
10563
+ planner: { ...plannerAgent, model: DEFAULT_MODEL },
10564
+ implementer: { ...implementerAgent, model: DEFAULT_MODEL },
10565
+ reviewer: { ...reviewerAgent, model: DEFAULT_MODEL },
10566
+ executor: { ...executorAgent, model: DEFAULT_MODEL },
10567
+ "ledger-creator": { ...ledgerCreatorAgent, model: DEFAULT_MODEL },
10568
+ "artifact-searcher": { ...artifactSearcherAgent, model: DEFAULT_MODEL },
10569
+ "project-initializer": { ...projectInitializerAgent, model: DEFAULT_MODEL },
10570
+ octto: { ...octtoAgent, model: DEFAULT_MODEL },
10571
+ probe: { ...probeAgent, model: DEFAULT_MODEL },
10572
+ "mm-stack-detector": { ...stackDetectorAgent, model: DEFAULT_MODEL },
10573
+ "mm-pattern-discoverer": { ...mindmodelPatternDiscovererAgent, model: DEFAULT_MODEL },
10574
+ "mm-example-extractor": { ...exampleExtractorAgent, model: DEFAULT_MODEL },
10575
+ "mm-orchestrator": { ...mindmodelOrchestratorAgent, model: DEFAULT_MODEL },
10576
+ "mm-dependency-mapper": { ...dependencyMapperAgent, model: DEFAULT_MODEL },
10577
+ "mm-convention-extractor": { ...conventionExtractorAgent, model: DEFAULT_MODEL },
10578
+ "mm-domain-extractor": { ...domainExtractorAgent, model: DEFAULT_MODEL },
10579
+ "mm-code-clusterer": { ...codeClustererAgent, model: DEFAULT_MODEL },
10580
+ "mm-anti-pattern-detector": { ...antiPatternDetectorAgent, model: DEFAULT_MODEL },
10581
+ "mm-constraint-writer": { ...constraintWriterAgent, model: DEFAULT_MODEL },
10582
+ "mm-constraint-reviewer": { ...constraintReviewerAgent, model: DEFAULT_MODEL }
10505
10583
  };
10506
10584
 
10507
10585
  // src/config-loader.ts
10508
- import { readFileSync } from "fs";
10586
+ import { existsSync, readFileSync } from "fs";
10509
10587
  import { readFile } from "fs/promises";
10510
10588
  import { homedir } from "os";
10511
10589
  import { join } from "path";
10512
- function loadOpencodeConfig(configDir) {
10513
- const baseDir = configDir ?? join(homedir(), ".config", "opencode");
10514
- try {
10515
- const configPath = join(baseDir, "opencode.json");
10516
- const content = readFileSync(configPath, "utf-8");
10517
- return JSON.parse(content);
10518
- } catch {
10519
- return null;
10520
- }
10521
- }
10522
- function loadAvailableModels(configDir) {
10523
- const availableModels = new Set;
10524
- const config = loadOpencodeConfig(configDir);
10525
- if (config?.provider) {
10526
- for (const [providerId, providerConfig] of Object.entries(config.provider)) {
10527
- if (providerConfig.models) {
10528
- for (const modelId of Object.keys(providerConfig.models)) {
10529
- availableModels.add(`${providerId}/${modelId}`);
10530
- }
10590
+
10591
+ // node_modules/jsonc-parser/lib/esm/impl/scanner.js
10592
+ function createScanner(text, ignoreTrivia = false) {
10593
+ const len = text.length;
10594
+ let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
10595
+ function scanHexDigits(count, exact) {
10596
+ let digits = 0;
10597
+ let value2 = 0;
10598
+ while (digits < count || !exact) {
10599
+ let ch = text.charCodeAt(pos);
10600
+ if (ch >= 48 && ch <= 57) {
10601
+ value2 = value2 * 16 + ch - 48;
10602
+ } else if (ch >= 65 && ch <= 70) {
10603
+ value2 = value2 * 16 + ch - 65 + 10;
10604
+ } else if (ch >= 97 && ch <= 102) {
10605
+ value2 = value2 * 16 + ch - 97 + 10;
10606
+ } else {
10607
+ break;
10531
10608
  }
10609
+ pos++;
10610
+ digits++;
10611
+ }
10612
+ if (digits < count) {
10613
+ value2 = -1;
10532
10614
  }
10615
+ return value2;
10533
10616
  }
10534
- return availableModels;
10535
- }
10536
- function loadDefaultModel(configDir) {
10537
- const config = loadOpencodeConfig(configDir);
10538
- return config?.model ?? null;
10539
- }
10540
- var SAFE_AGENT_PROPERTIES = ["model", "temperature", "maxTokens"];
10541
- var BUILTIN_MODELS = new Set(["opencode/big-pickle"]);
10542
- async function loadMicodeConfig(configDir) {
10543
- const baseDir = configDir ?? join(homedir(), ".config", "opencode");
10544
- const configPath = join(baseDir, "micode.json");
10545
- try {
10546
- const content = await readFile(configPath, "utf-8");
10547
- const parsed = JSON.parse(content);
10548
- const result = {};
10549
- if (parsed.agents && typeof parsed.agents === "object") {
10550
- const sanitizedAgents = {};
10551
- for (const [agentName, agentConfig] of Object.entries(parsed.agents)) {
10552
- if (agentConfig && typeof agentConfig === "object") {
10553
- const sanitized = {};
10554
- const config = agentConfig;
10555
- for (const prop of SAFE_AGENT_PROPERTIES) {
10556
- if (prop in config) {
10557
- sanitized[prop] = config[prop];
10558
- }
10559
- }
10560
- sanitizedAgents[agentName] = sanitized;
10561
- }
10617
+ function setPosition(newPosition) {
10618
+ pos = newPosition;
10619
+ value = "";
10620
+ tokenOffset = 0;
10621
+ token = 16;
10622
+ scanError = 0;
10623
+ }
10624
+ function scanNumber() {
10625
+ let start = pos;
10626
+ if (text.charCodeAt(pos) === 48) {
10627
+ pos++;
10628
+ } else {
10629
+ pos++;
10630
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
10631
+ pos++;
10562
10632
  }
10563
- result.agents = sanitizedAgents;
10564
- }
10565
- if (parsed.features && typeof parsed.features === "object") {
10566
- const features = parsed.features;
10567
- result.features = {
10568
- mindmodelInjection: features.mindmodelInjection === true
10569
- };
10570
10633
  }
10571
- if (typeof parsed.compactionThreshold === "number") {
10572
- const threshold = parsed.compactionThreshold;
10573
- if (threshold >= 0 && threshold <= 1) {
10574
- result.compactionThreshold = threshold;
10634
+ if (pos < text.length && text.charCodeAt(pos) === 46) {
10635
+ pos++;
10636
+ if (pos < text.length && isDigit(text.charCodeAt(pos))) {
10637
+ pos++;
10638
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
10639
+ pos++;
10640
+ }
10641
+ } else {
10642
+ scanError = 3;
10643
+ return text.substring(start, pos);
10575
10644
  }
10576
10645
  }
10577
- if (parsed.fragments && typeof parsed.fragments === "object") {
10578
- const fragments = parsed.fragments;
10579
- const sanitizedFragments = {};
10580
- for (const [agentName, fragmentList] of Object.entries(fragments)) {
10581
- if (Array.isArray(fragmentList)) {
10582
- const validFragments = fragmentList.filter((f) => typeof f === "string" && f.trim().length > 0);
10583
- if (validFragments.length > 0) {
10584
- sanitizedFragments[agentName] = validFragments;
10585
- }
10646
+ let end = pos;
10647
+ if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
10648
+ pos++;
10649
+ if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
10650
+ pos++;
10651
+ }
10652
+ if (pos < text.length && isDigit(text.charCodeAt(pos))) {
10653
+ pos++;
10654
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
10655
+ pos++;
10586
10656
  }
10657
+ end = pos;
10658
+ } else {
10659
+ scanError = 3;
10587
10660
  }
10588
- result.fragments = sanitizedFragments;
10589
10661
  }
10590
- return result;
10591
- } catch {
10592
- return null;
10662
+ return text.substring(start, end);
10593
10663
  }
10594
- }
10595
- function loadModelContextLimits(configDir) {
10596
- const limits = new Map;
10597
- const baseDir = configDir ?? join(homedir(), ".config", "opencode");
10598
- try {
10599
- const configPath = join(baseDir, "opencode.json");
10600
- const content = readFileSync(configPath, "utf-8");
10601
- const config = JSON.parse(content);
10602
- if (config.provider) {
10603
- for (const [providerId, providerConfig] of Object.entries(config.provider)) {
10604
- if (providerConfig.models) {
10605
- for (const [modelId, modelConfig] of Object.entries(providerConfig.models)) {
10606
- const contextLimit = modelConfig?.limit?.context;
10607
- if (typeof contextLimit === "number" && contextLimit > 0) {
10608
- limits.set(`${providerId}/${modelId}`, contextLimit);
10664
+ function scanString() {
10665
+ let result = "", start = pos;
10666
+ while (true) {
10667
+ if (pos >= len) {
10668
+ result += text.substring(start, pos);
10669
+ scanError = 2;
10670
+ break;
10671
+ }
10672
+ const ch = text.charCodeAt(pos);
10673
+ if (ch === 34) {
10674
+ result += text.substring(start, pos);
10675
+ pos++;
10676
+ break;
10677
+ }
10678
+ if (ch === 92) {
10679
+ result += text.substring(start, pos);
10680
+ pos++;
10681
+ if (pos >= len) {
10682
+ scanError = 2;
10683
+ break;
10684
+ }
10685
+ const ch2 = text.charCodeAt(pos++);
10686
+ switch (ch2) {
10687
+ case 34:
10688
+ result += '"';
10689
+ break;
10690
+ case 92:
10691
+ result += "\\";
10692
+ break;
10693
+ case 47:
10694
+ result += "/";
10695
+ break;
10696
+ case 98:
10697
+ result += "\b";
10698
+ break;
10699
+ case 102:
10700
+ result += "\f";
10701
+ break;
10702
+ case 110:
10703
+ result += `
10704
+ `;
10705
+ break;
10706
+ case 114:
10707
+ result += "\r";
10708
+ break;
10709
+ case 116:
10710
+ result += "\t";
10711
+ break;
10712
+ case 117:
10713
+ const ch3 = scanHexDigits(4, true);
10714
+ if (ch3 >= 0) {
10715
+ result += String.fromCharCode(ch3);
10716
+ } else {
10717
+ scanError = 4;
10609
10718
  }
10610
- }
10719
+ break;
10720
+ default:
10721
+ scanError = 5;
10611
10722
  }
10723
+ start = pos;
10724
+ continue;
10612
10725
  }
10613
- }
10614
- } catch {}
10615
- return limits;
10616
- }
10617
- function mergeAgentConfigs(pluginAgents, userConfig, availableModels, defaultModel) {
10618
- const models = availableModels ?? loadAvailableModels();
10619
- const shouldValidateModels = models.size > 0;
10620
- const opencodeDefaultModel = defaultModel ?? loadDefaultModel();
10621
- const isValidModel = (model) => {
10622
- if (BUILTIN_MODELS.has(model))
10623
- return true;
10624
- if (!shouldValidateModels)
10625
- return true;
10626
- return models.has(model);
10627
- };
10628
- const merged = {};
10629
- for (const [name, agentConfig] of Object.entries(pluginAgents)) {
10630
- const userOverride = userConfig?.agents?.[name];
10631
- let finalConfig = { ...agentConfig };
10632
- if (opencodeDefaultModel && isValidModel(opencodeDefaultModel)) {
10633
- finalConfig = { ...finalConfig, model: opencodeDefaultModel };
10634
- }
10635
- if (userOverride) {
10636
- if (userOverride.model) {
10637
- if (isValidModel(userOverride.model)) {
10638
- finalConfig = { ...finalConfig, ...userOverride };
10726
+ if (ch >= 0 && ch <= 31) {
10727
+ if (isLineBreak(ch)) {
10728
+ result += text.substring(start, pos);
10729
+ scanError = 2;
10730
+ break;
10639
10731
  } else {
10640
- console.warn(`[micode] Model "${userOverride.model}" for agent "${name}" is not available. Using opencode default.`);
10641
- const { model: _ignored, ...safeOverrides } = userOverride;
10642
- finalConfig = { ...finalConfig, ...safeOverrides };
10732
+ scanError = 6;
10643
10733
  }
10644
- } else {
10645
- finalConfig = { ...finalConfig, ...userOverride };
10646
10734
  }
10735
+ pos++;
10647
10736
  }
10648
- merged[name] = finalConfig;
10737
+ return result;
10649
10738
  }
10650
- return merged;
10651
- }
10652
-
10653
- // src/hooks/artifact-auto-index.ts
10654
- import { readFileSync as readFileSync3 } from "fs";
10655
-
10656
- // src/tools/artifact-index/index.ts
10657
- import { Database } from "bun:sqlite";
10658
- import { existsSync, mkdirSync, readFileSync as readFileSync2 } from "fs";
10659
- import { homedir as homedir2 } from "os";
10660
- import { dirname, join as join2 } from "path";
10661
- var DEFAULT_DB_DIR = join2(homedir2(), ".config", "opencode", "artifact-index");
10662
- var DB_NAME = "context.db";
10663
-
10664
- class ArtifactIndex {
10665
- db = null;
10666
- dbPath;
10667
- constructor(dbDir = DEFAULT_DB_DIR) {
10668
- this.dbPath = join2(dbDir, DB_NAME);
10739
+ function scanNext() {
10740
+ value = "";
10741
+ scanError = 0;
10742
+ tokenOffset = pos;
10743
+ lineStartOffset = lineNumber;
10744
+ prevTokenLineStartOffset = tokenLineStartOffset;
10745
+ if (pos >= len) {
10746
+ tokenOffset = len;
10747
+ return token = 17;
10748
+ }
10749
+ let code = text.charCodeAt(pos);
10750
+ if (isWhiteSpace(code)) {
10751
+ do {
10752
+ pos++;
10753
+ value += String.fromCharCode(code);
10754
+ code = text.charCodeAt(pos);
10755
+ } while (isWhiteSpace(code));
10756
+ return token = 15;
10757
+ }
10758
+ if (isLineBreak(code)) {
10759
+ pos++;
10760
+ value += String.fromCharCode(code);
10761
+ if (code === 13 && text.charCodeAt(pos) === 10) {
10762
+ pos++;
10763
+ value += `
10764
+ `;
10765
+ }
10766
+ lineNumber++;
10767
+ tokenLineStartOffset = pos;
10768
+ return token = 14;
10769
+ }
10770
+ switch (code) {
10771
+ case 123:
10772
+ pos++;
10773
+ return token = 1;
10774
+ case 125:
10775
+ pos++;
10776
+ return token = 2;
10777
+ case 91:
10778
+ pos++;
10779
+ return token = 3;
10780
+ case 93:
10781
+ pos++;
10782
+ return token = 4;
10783
+ case 58:
10784
+ pos++;
10785
+ return token = 6;
10786
+ case 44:
10787
+ pos++;
10788
+ return token = 5;
10789
+ case 34:
10790
+ pos++;
10791
+ value = scanString();
10792
+ return token = 10;
10793
+ case 47:
10794
+ const start = pos - 1;
10795
+ if (text.charCodeAt(pos + 1) === 47) {
10796
+ pos += 2;
10797
+ while (pos < len) {
10798
+ if (isLineBreak(text.charCodeAt(pos))) {
10799
+ break;
10800
+ }
10801
+ pos++;
10802
+ }
10803
+ value = text.substring(start, pos);
10804
+ return token = 12;
10805
+ }
10806
+ if (text.charCodeAt(pos + 1) === 42) {
10807
+ pos += 2;
10808
+ const safeLength = len - 1;
10809
+ let commentClosed = false;
10810
+ while (pos < safeLength) {
10811
+ const ch = text.charCodeAt(pos);
10812
+ if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
10813
+ pos += 2;
10814
+ commentClosed = true;
10815
+ break;
10816
+ }
10817
+ pos++;
10818
+ if (isLineBreak(ch)) {
10819
+ if (ch === 13 && text.charCodeAt(pos) === 10) {
10820
+ pos++;
10821
+ }
10822
+ lineNumber++;
10823
+ tokenLineStartOffset = pos;
10824
+ }
10825
+ }
10826
+ if (!commentClosed) {
10827
+ pos++;
10828
+ scanError = 1;
10829
+ }
10830
+ value = text.substring(start, pos);
10831
+ return token = 13;
10832
+ }
10833
+ value += String.fromCharCode(code);
10834
+ pos++;
10835
+ return token = 16;
10836
+ case 45:
10837
+ value += String.fromCharCode(code);
10838
+ pos++;
10839
+ if (pos === len || !isDigit(text.charCodeAt(pos))) {
10840
+ return token = 16;
10841
+ }
10842
+ case 48:
10843
+ case 49:
10844
+ case 50:
10845
+ case 51:
10846
+ case 52:
10847
+ case 53:
10848
+ case 54:
10849
+ case 55:
10850
+ case 56:
10851
+ case 57:
10852
+ value += scanNumber();
10853
+ return token = 11;
10854
+ default:
10855
+ while (pos < len && isUnknownContentCharacter(code)) {
10856
+ pos++;
10857
+ code = text.charCodeAt(pos);
10858
+ }
10859
+ if (tokenOffset !== pos) {
10860
+ value = text.substring(tokenOffset, pos);
10861
+ switch (value) {
10862
+ case "true":
10863
+ return token = 8;
10864
+ case "false":
10865
+ return token = 9;
10866
+ case "null":
10867
+ return token = 7;
10868
+ }
10869
+ return token = 16;
10870
+ }
10871
+ value += String.fromCharCode(code);
10872
+ pos++;
10873
+ return token = 16;
10874
+ }
10669
10875
  }
10670
- async initialize() {
10671
- const dir = dirname(this.dbPath);
10672
- if (!existsSync(dir)) {
10673
- mkdirSync(dir, { recursive: true });
10876
+ function isUnknownContentCharacter(code) {
10877
+ if (isWhiteSpace(code) || isLineBreak(code)) {
10878
+ return false;
10674
10879
  }
10675
- this.db = new Database(this.dbPath);
10676
- const schemaPath = join2(dirname(import.meta.path), "schema.sql");
10677
- let schema;
10678
- try {
10679
- schema = readFileSync2(schemaPath, "utf-8");
10680
- } catch {
10681
- schema = this.getInlineSchema();
10880
+ switch (code) {
10881
+ case 125:
10882
+ case 93:
10883
+ case 123:
10884
+ case 91:
10885
+ case 34:
10886
+ case 58:
10887
+ case 44:
10888
+ case 47:
10889
+ return false;
10682
10890
  }
10683
- this.db.exec(schema);
10891
+ return true;
10684
10892
  }
10685
- getInlineSchema() {
10686
- return `
10687
- CREATE TABLE IF NOT EXISTS plans (
10688
- id TEXT PRIMARY KEY,
10689
- title TEXT,
10690
- file_path TEXT UNIQUE NOT NULL,
10691
- overview TEXT,
10692
- approach TEXT,
10693
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10893
+ function scanNextNonTrivia() {
10894
+ let result;
10895
+ do {
10896
+ result = scanNext();
10897
+ } while (result >= 12 && result <= 15);
10898
+ return result;
10899
+ }
10900
+ return {
10901
+ setPosition,
10902
+ getPosition: () => pos,
10903
+ scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
10904
+ getToken: () => token,
10905
+ getTokenValue: () => value,
10906
+ getTokenOffset: () => tokenOffset,
10907
+ getTokenLength: () => pos - tokenOffset,
10908
+ getTokenStartLine: () => lineStartOffset,
10909
+ getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
10910
+ getTokenError: () => scanError
10911
+ };
10912
+ }
10913
+ function isWhiteSpace(ch) {
10914
+ return ch === 32 || ch === 9;
10915
+ }
10916
+ function isLineBreak(ch) {
10917
+ return ch === 10 || ch === 13;
10918
+ }
10919
+ function isDigit(ch) {
10920
+ return ch >= 48 && ch <= 57;
10921
+ }
10922
+ var CharacterCodes;
10923
+ (function(CharacterCodes2) {
10924
+ CharacterCodes2[CharacterCodes2["lineFeed"] = 10] = "lineFeed";
10925
+ CharacterCodes2[CharacterCodes2["carriageReturn"] = 13] = "carriageReturn";
10926
+ CharacterCodes2[CharacterCodes2["space"] = 32] = "space";
10927
+ CharacterCodes2[CharacterCodes2["_0"] = 48] = "_0";
10928
+ CharacterCodes2[CharacterCodes2["_1"] = 49] = "_1";
10929
+ CharacterCodes2[CharacterCodes2["_2"] = 50] = "_2";
10930
+ CharacterCodes2[CharacterCodes2["_3"] = 51] = "_3";
10931
+ CharacterCodes2[CharacterCodes2["_4"] = 52] = "_4";
10932
+ CharacterCodes2[CharacterCodes2["_5"] = 53] = "_5";
10933
+ CharacterCodes2[CharacterCodes2["_6"] = 54] = "_6";
10934
+ CharacterCodes2[CharacterCodes2["_7"] = 55] = "_7";
10935
+ CharacterCodes2[CharacterCodes2["_8"] = 56] = "_8";
10936
+ CharacterCodes2[CharacterCodes2["_9"] = 57] = "_9";
10937
+ CharacterCodes2[CharacterCodes2["a"] = 97] = "a";
10938
+ CharacterCodes2[CharacterCodes2["b"] = 98] = "b";
10939
+ CharacterCodes2[CharacterCodes2["c"] = 99] = "c";
10940
+ CharacterCodes2[CharacterCodes2["d"] = 100] = "d";
10941
+ CharacterCodes2[CharacterCodes2["e"] = 101] = "e";
10942
+ CharacterCodes2[CharacterCodes2["f"] = 102] = "f";
10943
+ CharacterCodes2[CharacterCodes2["g"] = 103] = "g";
10944
+ CharacterCodes2[CharacterCodes2["h"] = 104] = "h";
10945
+ CharacterCodes2[CharacterCodes2["i"] = 105] = "i";
10946
+ CharacterCodes2[CharacterCodes2["j"] = 106] = "j";
10947
+ CharacterCodes2[CharacterCodes2["k"] = 107] = "k";
10948
+ CharacterCodes2[CharacterCodes2["l"] = 108] = "l";
10949
+ CharacterCodes2[CharacterCodes2["m"] = 109] = "m";
10950
+ CharacterCodes2[CharacterCodes2["n"] = 110] = "n";
10951
+ CharacterCodes2[CharacterCodes2["o"] = 111] = "o";
10952
+ CharacterCodes2[CharacterCodes2["p"] = 112] = "p";
10953
+ CharacterCodes2[CharacterCodes2["q"] = 113] = "q";
10954
+ CharacterCodes2[CharacterCodes2["r"] = 114] = "r";
10955
+ CharacterCodes2[CharacterCodes2["s"] = 115] = "s";
10956
+ CharacterCodes2[CharacterCodes2["t"] = 116] = "t";
10957
+ CharacterCodes2[CharacterCodes2["u"] = 117] = "u";
10958
+ CharacterCodes2[CharacterCodes2["v"] = 118] = "v";
10959
+ CharacterCodes2[CharacterCodes2["w"] = 119] = "w";
10960
+ CharacterCodes2[CharacterCodes2["x"] = 120] = "x";
10961
+ CharacterCodes2[CharacterCodes2["y"] = 121] = "y";
10962
+ CharacterCodes2[CharacterCodes2["z"] = 122] = "z";
10963
+ CharacterCodes2[CharacterCodes2["A"] = 65] = "A";
10964
+ CharacterCodes2[CharacterCodes2["B"] = 66] = "B";
10965
+ CharacterCodes2[CharacterCodes2["C"] = 67] = "C";
10966
+ CharacterCodes2[CharacterCodes2["D"] = 68] = "D";
10967
+ CharacterCodes2[CharacterCodes2["E"] = 69] = "E";
10968
+ CharacterCodes2[CharacterCodes2["F"] = 70] = "F";
10969
+ CharacterCodes2[CharacterCodes2["G"] = 71] = "G";
10970
+ CharacterCodes2[CharacterCodes2["H"] = 72] = "H";
10971
+ CharacterCodes2[CharacterCodes2["I"] = 73] = "I";
10972
+ CharacterCodes2[CharacterCodes2["J"] = 74] = "J";
10973
+ CharacterCodes2[CharacterCodes2["K"] = 75] = "K";
10974
+ CharacterCodes2[CharacterCodes2["L"] = 76] = "L";
10975
+ CharacterCodes2[CharacterCodes2["M"] = 77] = "M";
10976
+ CharacterCodes2[CharacterCodes2["N"] = 78] = "N";
10977
+ CharacterCodes2[CharacterCodes2["O"] = 79] = "O";
10978
+ CharacterCodes2[CharacterCodes2["P"] = 80] = "P";
10979
+ CharacterCodes2[CharacterCodes2["Q"] = 81] = "Q";
10980
+ CharacterCodes2[CharacterCodes2["R"] = 82] = "R";
10981
+ CharacterCodes2[CharacterCodes2["S"] = 83] = "S";
10982
+ CharacterCodes2[CharacterCodes2["T"] = 84] = "T";
10983
+ CharacterCodes2[CharacterCodes2["U"] = 85] = "U";
10984
+ CharacterCodes2[CharacterCodes2["V"] = 86] = "V";
10985
+ CharacterCodes2[CharacterCodes2["W"] = 87] = "W";
10986
+ CharacterCodes2[CharacterCodes2["X"] = 88] = "X";
10987
+ CharacterCodes2[CharacterCodes2["Y"] = 89] = "Y";
10988
+ CharacterCodes2[CharacterCodes2["Z"] = 90] = "Z";
10989
+ CharacterCodes2[CharacterCodes2["asterisk"] = 42] = "asterisk";
10990
+ CharacterCodes2[CharacterCodes2["backslash"] = 92] = "backslash";
10991
+ CharacterCodes2[CharacterCodes2["closeBrace"] = 125] = "closeBrace";
10992
+ CharacterCodes2[CharacterCodes2["closeBracket"] = 93] = "closeBracket";
10993
+ CharacterCodes2[CharacterCodes2["colon"] = 58] = "colon";
10994
+ CharacterCodes2[CharacterCodes2["comma"] = 44] = "comma";
10995
+ CharacterCodes2[CharacterCodes2["dot"] = 46] = "dot";
10996
+ CharacterCodes2[CharacterCodes2["doubleQuote"] = 34] = "doubleQuote";
10997
+ CharacterCodes2[CharacterCodes2["minus"] = 45] = "minus";
10998
+ CharacterCodes2[CharacterCodes2["openBrace"] = 123] = "openBrace";
10999
+ CharacterCodes2[CharacterCodes2["openBracket"] = 91] = "openBracket";
11000
+ CharacterCodes2[CharacterCodes2["plus"] = 43] = "plus";
11001
+ CharacterCodes2[CharacterCodes2["slash"] = 47] = "slash";
11002
+ CharacterCodes2[CharacterCodes2["formFeed"] = 12] = "formFeed";
11003
+ CharacterCodes2[CharacterCodes2["tab"] = 9] = "tab";
11004
+ })(CharacterCodes || (CharacterCodes = {}));
11005
+
11006
+ // node_modules/jsonc-parser/lib/esm/impl/string-intern.js
11007
+ var cachedSpaces = new Array(20).fill(0).map((_, index) => {
11008
+ return " ".repeat(index);
11009
+ });
11010
+ var maxCachedValues = 200;
11011
+ var cachedBreakLinesWithSpaces = {
11012
+ " ": {
11013
+ "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
11014
+ return `
11015
+ ` + " ".repeat(index);
11016
+ }),
11017
+ "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
11018
+ return "\r" + " ".repeat(index);
11019
+ }),
11020
+ "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
11021
+ return `\r
11022
+ ` + " ".repeat(index);
11023
+ })
11024
+ },
11025
+ "\t": {
11026
+ "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
11027
+ return `
11028
+ ` + "\t".repeat(index);
11029
+ }),
11030
+ "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
11031
+ return "\r" + "\t".repeat(index);
11032
+ }),
11033
+ "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
11034
+ return `\r
11035
+ ` + "\t".repeat(index);
11036
+ })
11037
+ }
11038
+ };
11039
+
11040
+ // node_modules/jsonc-parser/lib/esm/impl/parser.js
11041
+ var ParseOptions;
11042
+ (function(ParseOptions2) {
11043
+ ParseOptions2.DEFAULT = {
11044
+ allowTrailingComma: false
11045
+ };
11046
+ })(ParseOptions || (ParseOptions = {}));
11047
+ function parse(text, errors = [], options = ParseOptions.DEFAULT) {
11048
+ let currentProperty = null;
11049
+ let currentParent = [];
11050
+ const previousParents = [];
11051
+ function onValue(value) {
11052
+ if (Array.isArray(currentParent)) {
11053
+ currentParent.push(value);
11054
+ } else if (currentProperty !== null) {
11055
+ currentParent[currentProperty] = value;
11056
+ }
11057
+ }
11058
+ const visitor = {
11059
+ onObjectBegin: () => {
11060
+ const object = {};
11061
+ onValue(object);
11062
+ previousParents.push(currentParent);
11063
+ currentParent = object;
11064
+ currentProperty = null;
11065
+ },
11066
+ onObjectProperty: (name) => {
11067
+ currentProperty = name;
11068
+ },
11069
+ onObjectEnd: () => {
11070
+ currentParent = previousParents.pop();
11071
+ },
11072
+ onArrayBegin: () => {
11073
+ const array = [];
11074
+ onValue(array);
11075
+ previousParents.push(currentParent);
11076
+ currentParent = array;
11077
+ currentProperty = null;
11078
+ },
11079
+ onArrayEnd: () => {
11080
+ currentParent = previousParents.pop();
11081
+ },
11082
+ onLiteralValue: onValue,
11083
+ onError: (error, offset, length) => {
11084
+ errors.push({ error, offset, length });
11085
+ }
11086
+ };
11087
+ visit(text, visitor, options);
11088
+ return currentParent[0];
11089
+ }
11090
+ function visit(text, visitor, options = ParseOptions.DEFAULT) {
11091
+ const _scanner = createScanner(text, false);
11092
+ const _jsonPath = [];
11093
+ let suppressedCallbacks = 0;
11094
+ function toNoArgVisit(visitFunction) {
11095
+ return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
11096
+ }
11097
+ function toOneArgVisit(visitFunction) {
11098
+ return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
11099
+ }
11100
+ function toOneArgVisitWithPath(visitFunction) {
11101
+ return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
11102
+ }
11103
+ function toBeginVisit(visitFunction) {
11104
+ return visitFunction ? () => {
11105
+ if (suppressedCallbacks > 0) {
11106
+ suppressedCallbacks++;
11107
+ } else {
11108
+ let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
11109
+ if (cbReturn === false) {
11110
+ suppressedCallbacks = 1;
11111
+ }
11112
+ }
11113
+ } : () => true;
11114
+ }
11115
+ function toEndVisit(visitFunction) {
11116
+ return visitFunction ? () => {
11117
+ if (suppressedCallbacks > 0) {
11118
+ suppressedCallbacks--;
11119
+ }
11120
+ if (suppressedCallbacks === 0) {
11121
+ visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
11122
+ }
11123
+ } : () => true;
11124
+ }
11125
+ const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
11126
+ const disallowComments = options && options.disallowComments;
11127
+ const allowTrailingComma = options && options.allowTrailingComma;
11128
+ function scanNext() {
11129
+ while (true) {
11130
+ const token = _scanner.scan();
11131
+ switch (_scanner.getTokenError()) {
11132
+ case 4:
11133
+ handleError(14);
11134
+ break;
11135
+ case 5:
11136
+ handleError(15);
11137
+ break;
11138
+ case 3:
11139
+ handleError(13);
11140
+ break;
11141
+ case 1:
11142
+ if (!disallowComments) {
11143
+ handleError(11);
11144
+ }
11145
+ break;
11146
+ case 2:
11147
+ handleError(12);
11148
+ break;
11149
+ case 6:
11150
+ handleError(16);
11151
+ break;
11152
+ }
11153
+ switch (token) {
11154
+ case 12:
11155
+ case 13:
11156
+ if (disallowComments) {
11157
+ handleError(10);
11158
+ } else {
11159
+ onComment();
11160
+ }
11161
+ break;
11162
+ case 16:
11163
+ handleError(1);
11164
+ break;
11165
+ case 15:
11166
+ case 14:
11167
+ break;
11168
+ default:
11169
+ return token;
11170
+ }
11171
+ }
11172
+ }
11173
+ function handleError(error, skipUntilAfter = [], skipUntil = []) {
11174
+ onError(error);
11175
+ if (skipUntilAfter.length + skipUntil.length > 0) {
11176
+ let token = _scanner.getToken();
11177
+ while (token !== 17) {
11178
+ if (skipUntilAfter.indexOf(token) !== -1) {
11179
+ scanNext();
11180
+ break;
11181
+ } else if (skipUntil.indexOf(token) !== -1) {
11182
+ break;
11183
+ }
11184
+ token = scanNext();
11185
+ }
11186
+ }
11187
+ }
11188
+ function parseString(isValue) {
11189
+ const value = _scanner.getTokenValue();
11190
+ if (isValue) {
11191
+ onLiteralValue(value);
11192
+ } else {
11193
+ onObjectProperty(value);
11194
+ _jsonPath.push(value);
11195
+ }
11196
+ scanNext();
11197
+ return true;
11198
+ }
11199
+ function parseLiteral() {
11200
+ switch (_scanner.getToken()) {
11201
+ case 11:
11202
+ const tokenValue = _scanner.getTokenValue();
11203
+ let value = Number(tokenValue);
11204
+ if (isNaN(value)) {
11205
+ handleError(2);
11206
+ value = 0;
11207
+ }
11208
+ onLiteralValue(value);
11209
+ break;
11210
+ case 7:
11211
+ onLiteralValue(null);
11212
+ break;
11213
+ case 8:
11214
+ onLiteralValue(true);
11215
+ break;
11216
+ case 9:
11217
+ onLiteralValue(false);
11218
+ break;
11219
+ default:
11220
+ return false;
11221
+ }
11222
+ scanNext();
11223
+ return true;
11224
+ }
11225
+ function parseProperty() {
11226
+ if (_scanner.getToken() !== 10) {
11227
+ handleError(3, [], [2, 5]);
11228
+ return false;
11229
+ }
11230
+ parseString(false);
11231
+ if (_scanner.getToken() === 6) {
11232
+ onSeparator(":");
11233
+ scanNext();
11234
+ if (!parseValue()) {
11235
+ handleError(4, [], [2, 5]);
11236
+ }
11237
+ } else {
11238
+ handleError(5, [], [2, 5]);
11239
+ }
11240
+ _jsonPath.pop();
11241
+ return true;
11242
+ }
11243
+ function parseObject() {
11244
+ onObjectBegin();
11245
+ scanNext();
11246
+ let needsComma = false;
11247
+ while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
11248
+ if (_scanner.getToken() === 5) {
11249
+ if (!needsComma) {
11250
+ handleError(4, [], []);
11251
+ }
11252
+ onSeparator(",");
11253
+ scanNext();
11254
+ if (_scanner.getToken() === 2 && allowTrailingComma) {
11255
+ break;
11256
+ }
11257
+ } else if (needsComma) {
11258
+ handleError(6, [], []);
11259
+ }
11260
+ if (!parseProperty()) {
11261
+ handleError(4, [], [2, 5]);
11262
+ }
11263
+ needsComma = true;
11264
+ }
11265
+ onObjectEnd();
11266
+ if (_scanner.getToken() !== 2) {
11267
+ handleError(7, [2], []);
11268
+ } else {
11269
+ scanNext();
11270
+ }
11271
+ return true;
11272
+ }
11273
+ function parseArray() {
11274
+ onArrayBegin();
11275
+ scanNext();
11276
+ let isFirstElement = true;
11277
+ let needsComma = false;
11278
+ while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
11279
+ if (_scanner.getToken() === 5) {
11280
+ if (!needsComma) {
11281
+ handleError(4, [], []);
11282
+ }
11283
+ onSeparator(",");
11284
+ scanNext();
11285
+ if (_scanner.getToken() === 4 && allowTrailingComma) {
11286
+ break;
11287
+ }
11288
+ } else if (needsComma) {
11289
+ handleError(6, [], []);
11290
+ }
11291
+ if (isFirstElement) {
11292
+ _jsonPath.push(0);
11293
+ isFirstElement = false;
11294
+ } else {
11295
+ _jsonPath[_jsonPath.length - 1]++;
11296
+ }
11297
+ if (!parseValue()) {
11298
+ handleError(4, [], [4, 5]);
11299
+ }
11300
+ needsComma = true;
11301
+ }
11302
+ onArrayEnd();
11303
+ if (!isFirstElement) {
11304
+ _jsonPath.pop();
11305
+ }
11306
+ if (_scanner.getToken() !== 4) {
11307
+ handleError(8, [4], []);
11308
+ } else {
11309
+ scanNext();
11310
+ }
11311
+ return true;
11312
+ }
11313
+ function parseValue() {
11314
+ switch (_scanner.getToken()) {
11315
+ case 3:
11316
+ return parseArray();
11317
+ case 1:
11318
+ return parseObject();
11319
+ case 10:
11320
+ return parseString(true);
11321
+ default:
11322
+ return parseLiteral();
11323
+ }
11324
+ }
11325
+ scanNext();
11326
+ if (_scanner.getToken() === 17) {
11327
+ if (options.allowEmptyContent) {
11328
+ return true;
11329
+ }
11330
+ handleError(4, [], []);
11331
+ return false;
11332
+ }
11333
+ if (!parseValue()) {
11334
+ handleError(4, [], []);
11335
+ return false;
11336
+ }
11337
+ if (_scanner.getToken() !== 17) {
11338
+ handleError(9, [], []);
11339
+ }
11340
+ return true;
11341
+ }
11342
+
11343
+ // node_modules/jsonc-parser/lib/esm/main.js
11344
+ var ScanError;
11345
+ (function(ScanError2) {
11346
+ ScanError2[ScanError2["None"] = 0] = "None";
11347
+ ScanError2[ScanError2["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
11348
+ ScanError2[ScanError2["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
11349
+ ScanError2[ScanError2["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
11350
+ ScanError2[ScanError2["InvalidUnicode"] = 4] = "InvalidUnicode";
11351
+ ScanError2[ScanError2["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
11352
+ ScanError2[ScanError2["InvalidCharacter"] = 6] = "InvalidCharacter";
11353
+ })(ScanError || (ScanError = {}));
11354
+ var SyntaxKind;
11355
+ (function(SyntaxKind2) {
11356
+ SyntaxKind2[SyntaxKind2["OpenBraceToken"] = 1] = "OpenBraceToken";
11357
+ SyntaxKind2[SyntaxKind2["CloseBraceToken"] = 2] = "CloseBraceToken";
11358
+ SyntaxKind2[SyntaxKind2["OpenBracketToken"] = 3] = "OpenBracketToken";
11359
+ SyntaxKind2[SyntaxKind2["CloseBracketToken"] = 4] = "CloseBracketToken";
11360
+ SyntaxKind2[SyntaxKind2["CommaToken"] = 5] = "CommaToken";
11361
+ SyntaxKind2[SyntaxKind2["ColonToken"] = 6] = "ColonToken";
11362
+ SyntaxKind2[SyntaxKind2["NullKeyword"] = 7] = "NullKeyword";
11363
+ SyntaxKind2[SyntaxKind2["TrueKeyword"] = 8] = "TrueKeyword";
11364
+ SyntaxKind2[SyntaxKind2["FalseKeyword"] = 9] = "FalseKeyword";
11365
+ SyntaxKind2[SyntaxKind2["StringLiteral"] = 10] = "StringLiteral";
11366
+ SyntaxKind2[SyntaxKind2["NumericLiteral"] = 11] = "NumericLiteral";
11367
+ SyntaxKind2[SyntaxKind2["LineCommentTrivia"] = 12] = "LineCommentTrivia";
11368
+ SyntaxKind2[SyntaxKind2["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
11369
+ SyntaxKind2[SyntaxKind2["LineBreakTrivia"] = 14] = "LineBreakTrivia";
11370
+ SyntaxKind2[SyntaxKind2["Trivia"] = 15] = "Trivia";
11371
+ SyntaxKind2[SyntaxKind2["Unknown"] = 16] = "Unknown";
11372
+ SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
11373
+ })(SyntaxKind || (SyntaxKind = {}));
11374
+ var parse2 = parse;
11375
+ var ParseErrorCode;
11376
+ (function(ParseErrorCode2) {
11377
+ ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
11378
+ ParseErrorCode2[ParseErrorCode2["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
11379
+ ParseErrorCode2[ParseErrorCode2["PropertyNameExpected"] = 3] = "PropertyNameExpected";
11380
+ ParseErrorCode2[ParseErrorCode2["ValueExpected"] = 4] = "ValueExpected";
11381
+ ParseErrorCode2[ParseErrorCode2["ColonExpected"] = 5] = "ColonExpected";
11382
+ ParseErrorCode2[ParseErrorCode2["CommaExpected"] = 6] = "CommaExpected";
11383
+ ParseErrorCode2[ParseErrorCode2["CloseBraceExpected"] = 7] = "CloseBraceExpected";
11384
+ ParseErrorCode2[ParseErrorCode2["CloseBracketExpected"] = 8] = "CloseBracketExpected";
11385
+ ParseErrorCode2[ParseErrorCode2["EndOfFileExpected"] = 9] = "EndOfFileExpected";
11386
+ ParseErrorCode2[ParseErrorCode2["InvalidCommentToken"] = 10] = "InvalidCommentToken";
11387
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
11388
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
11389
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
11390
+ ParseErrorCode2[ParseErrorCode2["InvalidUnicode"] = 14] = "InvalidUnicode";
11391
+ ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
11392
+ ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
11393
+ })(ParseErrorCode || (ParseErrorCode = {}));
11394
+
11395
+ // src/config-loader.ts
11396
+ function parseConfigJson(content) {
11397
+ const errors = [];
11398
+ const result = parse2(content, errors, { allowTrailingComma: true });
11399
+ if (errors.length > 0) {
11400
+ throw new Error(`Invalid JSON/JSONC: ${errors.length} parse error(s)`);
11401
+ }
11402
+ return result;
11403
+ }
11404
+ function resolveConfigFileSync(baseDir, baseName) {
11405
+ const jsoncPath = join(baseDir, `${baseName}.jsonc`);
11406
+ if (existsSync(jsoncPath)) {
11407
+ return jsoncPath;
11408
+ }
11409
+ const jsonPath = join(baseDir, `${baseName}.json`);
11410
+ if (existsSync(jsonPath)) {
11411
+ return jsonPath;
11412
+ }
11413
+ return null;
11414
+ }
11415
+ async function readConfigFileAsync(baseDir, baseName) {
11416
+ try {
11417
+ return await readFile(join(baseDir, `${baseName}.jsonc`), "utf-8");
11418
+ } catch {}
11419
+ try {
11420
+ return await readFile(join(baseDir, `${baseName}.json`), "utf-8");
11421
+ } catch {
11422
+ return null;
11423
+ }
11424
+ }
11425
+ function loadOpencodeConfig(configDir) {
11426
+ const baseDir = configDir ?? join(homedir(), ".config", "opencode");
11427
+ try {
11428
+ const configPath = resolveConfigFileSync(baseDir, "opencode");
11429
+ if (!configPath)
11430
+ return null;
11431
+ const content = readFileSync(configPath, "utf-8");
11432
+ return parseConfigJson(content);
11433
+ } catch {
11434
+ return null;
11435
+ }
11436
+ }
11437
+ function loadAvailableModels(configDir) {
11438
+ const availableModels = new Set;
11439
+ const config2 = loadOpencodeConfig(configDir);
11440
+ if (config2?.provider) {
11441
+ for (const [providerId, providerConfig] of Object.entries(config2.provider)) {
11442
+ if (providerConfig.models) {
11443
+ for (const modelId of Object.keys(providerConfig.models)) {
11444
+ availableModels.add(`${providerId}/${modelId}`);
11445
+ }
11446
+ }
11447
+ }
11448
+ }
11449
+ return availableModels;
11450
+ }
11451
+ function loadDefaultModel(configDir) {
11452
+ const config2 = loadOpencodeConfig(configDir);
11453
+ return config2?.model ?? null;
11454
+ }
11455
+ var SAFE_AGENT_PROPERTIES = ["model", "temperature", "maxTokens", "thinking"];
11456
+ var BUILTIN_MODELS = new Set(["opencode/big-pickle"]);
11457
+ async function loadMicodeConfig(configDir) {
11458
+ const baseDir = configDir ?? join(homedir(), ".config", "opencode");
11459
+ try {
11460
+ const content = await readConfigFileAsync(baseDir, "micode");
11461
+ if (!content)
11462
+ return null;
11463
+ const parsed = parseConfigJson(content);
11464
+ const result = {};
11465
+ if (parsed.agents && typeof parsed.agents === "object") {
11466
+ const sanitizedAgents = {};
11467
+ for (const [agentName, agentConfig] of Object.entries(parsed.agents)) {
11468
+ if (agentConfig && typeof agentConfig === "object") {
11469
+ const sanitized = {};
11470
+ const config2 = agentConfig;
11471
+ for (const prop of SAFE_AGENT_PROPERTIES) {
11472
+ if (prop in config2) {
11473
+ sanitized[prop] = config2[prop];
11474
+ }
11475
+ }
11476
+ sanitizedAgents[agentName] = sanitized;
11477
+ }
11478
+ }
11479
+ result.agents = sanitizedAgents;
11480
+ }
11481
+ if (parsed.features && typeof parsed.features === "object") {
11482
+ const features = parsed.features;
11483
+ result.features = {
11484
+ mindmodelInjection: features.mindmodelInjection === true
11485
+ };
11486
+ }
11487
+ if (typeof parsed.compactionThreshold === "number") {
11488
+ const threshold = parsed.compactionThreshold;
11489
+ if (threshold >= 0 && threshold <= 1) {
11490
+ result.compactionThreshold = threshold;
11491
+ }
11492
+ }
11493
+ if (parsed.fragments && typeof parsed.fragments === "object") {
11494
+ const fragments = parsed.fragments;
11495
+ const sanitizedFragments = {};
11496
+ for (const [agentName, fragmentList] of Object.entries(fragments)) {
11497
+ if (Array.isArray(fragmentList)) {
11498
+ const validFragments = fragmentList.filter((f) => typeof f === "string" && f.trim().length > 0);
11499
+ if (validFragments.length > 0) {
11500
+ sanitizedFragments[agentName] = validFragments;
11501
+ }
11502
+ }
11503
+ }
11504
+ result.fragments = sanitizedFragments;
11505
+ }
11506
+ return result;
11507
+ } catch {
11508
+ return null;
11509
+ }
11510
+ }
11511
+ function loadModelContextLimits(configDir) {
11512
+ const limits = new Map;
11513
+ const baseDir = configDir ?? join(homedir(), ".config", "opencode");
11514
+ try {
11515
+ const configPath = resolveConfigFileSync(baseDir, "opencode");
11516
+ if (!configPath)
11517
+ return limits;
11518
+ const content = readFileSync(configPath, "utf-8");
11519
+ const config2 = parseConfigJson(content);
11520
+ if (config2.provider) {
11521
+ for (const [providerId, providerConfig] of Object.entries(config2.provider)) {
11522
+ if (providerConfig.models) {
11523
+ for (const [modelId, modelConfig] of Object.entries(providerConfig.models)) {
11524
+ const contextLimit = modelConfig?.limit?.context;
11525
+ if (typeof contextLimit === "number" && contextLimit > 0) {
11526
+ limits.set(`${providerId}/${modelId}`, contextLimit);
11527
+ }
11528
+ }
11529
+ }
11530
+ }
11531
+ }
11532
+ } catch {}
11533
+ return limits;
11534
+ }
11535
+ function mergeAgentConfigs(pluginAgents, userConfig, availableModels, defaultModel) {
11536
+ const models = availableModels ?? loadAvailableModels();
11537
+ const shouldValidateModels = models.size > 0;
11538
+ const opencodeDefaultModel = defaultModel ?? loadDefaultModel();
11539
+ const isValidModel = (model) => {
11540
+ if (BUILTIN_MODELS.has(model))
11541
+ return true;
11542
+ if (!shouldValidateModels)
11543
+ return true;
11544
+ return models.has(model);
11545
+ };
11546
+ const merged = {};
11547
+ for (const [name, agentConfig] of Object.entries(pluginAgents)) {
11548
+ const userOverride = userConfig?.agents?.[name];
11549
+ let finalConfig = { ...agentConfig };
11550
+ if (opencodeDefaultModel && isValidModel(opencodeDefaultModel)) {
11551
+ finalConfig = { ...finalConfig, model: opencodeDefaultModel };
11552
+ }
11553
+ if (userOverride) {
11554
+ if (userOverride.model) {
11555
+ if (isValidModel(userOverride.model)) {
11556
+ finalConfig = { ...finalConfig, ...userOverride };
11557
+ } else {
11558
+ const fallbackModel = finalConfig.model || "DEFAULT_MODEL";
11559
+ console.warn(`[micode] Model "${userOverride.model}" for agent "${name}" is not available. Using ${fallbackModel}.`);
11560
+ const { model: _ignored, ...safeOverrides } = userOverride;
11561
+ finalConfig = { ...finalConfig, ...safeOverrides };
11562
+ }
11563
+ } else {
11564
+ finalConfig = { ...finalConfig, ...userOverride };
11565
+ }
11566
+ }
11567
+ merged[name] = finalConfig;
11568
+ }
11569
+ return merged;
11570
+ }
11571
+
11572
+ // src/hooks/artifact-auto-index.ts
11573
+ import { readFileSync as readFileSync3 } from "fs";
11574
+
11575
+ // src/tools/artifact-index/index.ts
11576
+ import { Database } from "bun:sqlite";
11577
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2 } from "fs";
11578
+ import { homedir as homedir2 } from "os";
11579
+ import { dirname, join as join2 } from "path";
11580
+ var DEFAULT_DB_DIR = join2(homedir2(), ".config", "opencode", "artifact-index");
11581
+ var DB_NAME = "context.db";
11582
+
11583
+ class ArtifactIndex {
11584
+ db = null;
11585
+ dbPath;
11586
+ constructor(dbDir = DEFAULT_DB_DIR) {
11587
+ this.dbPath = join2(dbDir, DB_NAME);
11588
+ }
11589
+ async initialize() {
11590
+ const dir = dirname(this.dbPath);
11591
+ if (!existsSync2(dir)) {
11592
+ mkdirSync(dir, { recursive: true });
11593
+ }
11594
+ this.db = new Database(this.dbPath);
11595
+ const schemaPath = join2(dirname(import.meta.path), "schema.sql");
11596
+ let schema;
11597
+ try {
11598
+ schema = readFileSync2(schemaPath, "utf-8");
11599
+ } catch {
11600
+ schema = this.getInlineSchema();
11601
+ }
11602
+ this.db.exec(schema);
11603
+ }
11604
+ getInlineSchema() {
11605
+ return `
11606
+ CREATE TABLE IF NOT EXISTS plans (
11607
+ id TEXT PRIMARY KEY,
11608
+ title TEXT,
11609
+ file_path TEXT UNIQUE NOT NULL,
11610
+ overview TEXT,
11611
+ approach TEXT,
11612
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10694
11613
  indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
10695
11614
  );
10696
11615
  CREATE TABLE IF NOT EXISTS ledgers (
@@ -11025,79 +11944,16 @@ function createArtifactAutoIndexHook(_ctx) {
11025
11944
  await index.indexPlan(record);
11026
11945
  return;
11027
11946
  }
11028
- } catch (e) {
11029
- log.error("artifact-auto-index", `Error indexing ${filePath}`, e);
11030
- }
11031
- }
11032
- };
11033
- }
11034
-
11035
- // src/hooks/auto-compact.ts
11036
- import { mkdir, writeFile } from "fs/promises";
11037
- import { join as join3 } from "path";
11038
-
11039
- // src/utils/config.ts
11040
- var config = {
11041
- compaction: {
11042
- threshold: 0.7,
11043
- cooldownMs: 120000,
11044
- timeoutMs: 120000
11045
- },
11046
- contextWindow: {
11047
- warningThreshold: 0.7,
11048
- criticalThreshold: 0.85,
11049
- warningCooldownMs: 120000
11050
- },
11051
- tokens: {
11052
- charsPerToken: 4,
11053
- defaultContextLimit: 200000,
11054
- defaultMaxOutputTokens: 50000,
11055
- safetyMargin: 0.5,
11056
- preserveHeaderLines: 3
11057
- },
11058
- paths: {
11059
- ledgerDir: "thoughts/ledgers",
11060
- ledgerPrefix: "CONTINUITY_",
11061
- rootContextFiles: ["README.md", "ARCHITECTURE.md", "CODE_STYLE.md"],
11062
- dirContextFiles: ["README.md"],
11063
- planPattern: /thoughts\/shared\/plans\/.*\.md$/,
11064
- ledgerPattern: /thoughts\/ledgers\/CONTINUITY_.*\.md$/,
11065
- mindmodelDir: ".mindmodel",
11066
- mindmodelManifest: "manifest.yaml",
11067
- mindmodelSystem: "system.md"
11068
- },
11069
- timeouts: {
11070
- btcaMs: 120000,
11071
- toastSuccessMs: 3000,
11072
- toastWarningMs: 4000,
11073
- toastErrorMs: 5000
11074
- },
11075
- limits: {
11076
- largeFileBytes: 100 * 1024,
11077
- maxLinesNoExtract: 200,
11078
- ptyMaxBufferLines: 50000,
11079
- ptyDefaultReadLimit: 500,
11080
- ptyMaxLineLength: 2000,
11081
- astGrepMaxMatches: 100,
11082
- contextCacheTtlMs: 30000,
11083
- contextCacheMaxSize: 100
11084
- },
11085
- octto: {
11086
- answerTimeoutMs: 5 * 60 * 1000,
11087
- reviewTimeoutMs: 10 * 60 * 1000,
11088
- maxIterations: 50,
11089
- maxQuestions: 15,
11090
- stateDir: "thoughts/brainstorms",
11091
- bindAddress: "127.0.0.1",
11092
- allowRemoteBind: false
11093
- },
11094
- mindmodel: {
11095
- overrideLogFile: "overrides.log",
11096
- reviewMaxRetries: 1,
11097
- reviewEnabled: true,
11098
- categoryGroups: ["stack", "architecture", "patterns", "style", "components", "domain", "ops"]
11099
- }
11100
- };
11947
+ } catch (e) {
11948
+ log.error("artifact-auto-index", `Error indexing ${filePath}`, e);
11949
+ }
11950
+ }
11951
+ };
11952
+ }
11953
+
11954
+ // src/hooks/auto-compact.ts
11955
+ import { mkdir, writeFile } from "fs/promises";
11956
+ import { join as join3 } from "path";
11101
11957
 
11102
11958
  // src/utils/errors.ts
11103
11959
  function extractErrorMessage(e) {
@@ -11719,7 +12575,7 @@ function string(message$1) {
11719
12575
  }
11720
12576
  };
11721
12577
  }
11722
- function parse(schema, input, config$1) {
12578
+ function parse3(schema, input, config$1) {
11723
12579
  const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
11724
12580
  if (dataset.issues)
11725
12581
  throw new ValiError(dataset.issues);
@@ -11763,7 +12619,7 @@ var lexer = require_lexer();
11763
12619
  var lineCounter = require_line_counter();
11764
12620
  var parser = require_parser();
11765
12621
  var publicApi = require_public_api();
11766
- var visit = require_visit();
12622
+ var visit2 = require_visit();
11767
12623
  var $Composer = composer.Composer;
11768
12624
  var $Document = Document.Document;
11769
12625
  var $Schema = Schema.Schema;
@@ -11790,8 +12646,8 @@ var $parse = publicApi.parse;
11790
12646
  var $parseAllDocuments = publicApi.parseAllDocuments;
11791
12647
  var $parseDocument = publicApi.parseDocument;
11792
12648
  var $stringify = publicApi.stringify;
11793
- var $visit = visit.visit;
11794
- var $visitAsync = visit.visitAsync;
12649
+ var $visit = visit2.visit;
12650
+ var $visitAsync = visit2.visitAsync;
11795
12651
 
11796
12652
  // src/mindmodel/types.ts
11797
12653
  var CategorySchema = object({
@@ -11806,7 +12662,7 @@ var ManifestSchema = object({
11806
12662
  });
11807
12663
  function parseManifest(yamlContent) {
11808
12664
  const parsed = $parse(yamlContent);
11809
- return parse(ManifestSchema, parsed);
12665
+ return parse3(ManifestSchema, parsed);
11810
12666
  }
11811
12667
 
11812
12668
  // src/mindmodel/loader.ts
@@ -11826,7 +12682,7 @@ async function loadMindmodel(projectDir) {
11826
12682
  manifest
11827
12683
  };
11828
12684
  } catch (error) {
11829
- console.warn(`[micode] Failed to load mindmodel manifest: ${error}`);
12685
+ log.warn("mindmodel", `Failed to load manifest: ${error}`);
11830
12686
  return null;
11831
12687
  }
11832
12688
  }
@@ -11845,7 +12701,7 @@ async function loadExamples(mindmodel, categoryPaths) {
11845
12701
  content
11846
12702
  });
11847
12703
  } catch {
11848
- console.warn(`[micode] Failed to load mindmodel example: ${categoryPath}`);
12704
+ log.warn("mindmodel", `Failed to load example: ${categoryPath}`);
11849
12705
  }
11850
12706
  }
11851
12707
  return examples;
@@ -12204,6 +13060,165 @@ function createContextWindowMonitorHook(ctx, hookConfig) {
12204
13060
  };
12205
13061
  }
12206
13062
 
13063
+ // src/hooks/fetch-tracker.ts
13064
+ var FETCH_TOOLS = new Set(["webfetch", "context7_query-docs", "context7_resolve-library-id", "btca_ask"]);
13065
+
13066
+ class LRUCache {
13067
+ maxSize;
13068
+ cache = new Map;
13069
+ constructor(maxSize) {
13070
+ this.maxSize = maxSize;
13071
+ }
13072
+ get(key) {
13073
+ const value = this.cache.get(key);
13074
+ if (value !== undefined) {
13075
+ this.cache.delete(key);
13076
+ this.cache.set(key, value);
13077
+ }
13078
+ return value;
13079
+ }
13080
+ set(key, value) {
13081
+ if (this.cache.has(key)) {
13082
+ this.cache.delete(key);
13083
+ } else if (this.cache.size >= this.maxSize) {
13084
+ const firstKey = this.cache.keys().next().value;
13085
+ if (firstKey !== undefined)
13086
+ this.cache.delete(firstKey);
13087
+ }
13088
+ this.cache.set(key, value);
13089
+ }
13090
+ delete(key) {
13091
+ this.cache.delete(key);
13092
+ }
13093
+ clear() {
13094
+ this.cache.clear();
13095
+ }
13096
+ }
13097
+ var sessionCallCounts = new Map;
13098
+ var sessionCaches = new Map;
13099
+ function normalizeKey(tool, args) {
13100
+ if (!FETCH_TOOLS.has(tool) || !args)
13101
+ return null;
13102
+ try {
13103
+ switch (tool) {
13104
+ case "webfetch": {
13105
+ const rawUrl = args.url;
13106
+ if (!rawUrl)
13107
+ return null;
13108
+ try {
13109
+ const parsed = new URL(rawUrl);
13110
+ parsed.searchParams.sort();
13111
+ return `webfetch|${parsed.toString()}`;
13112
+ } catch {
13113
+ return `webfetch|${rawUrl}`;
13114
+ }
13115
+ }
13116
+ case "context7_query-docs": {
13117
+ const libraryId = args.libraryId;
13118
+ const query = args.query;
13119
+ if (!libraryId || !query)
13120
+ return null;
13121
+ return `context7_query-docs|${libraryId}|${query}`;
13122
+ }
13123
+ case "context7_resolve-library-id": {
13124
+ const libraryName = args.libraryName;
13125
+ const query = args.query;
13126
+ if (!libraryName || !query)
13127
+ return null;
13128
+ return `context7_resolve-library-id|${libraryName}|${query}`;
13129
+ }
13130
+ case "btca_ask": {
13131
+ const tech = args.tech;
13132
+ const question = args.question;
13133
+ if (!tech || !question)
13134
+ return null;
13135
+ return `btca_ask|${tech}|${question}`;
13136
+ }
13137
+ default:
13138
+ return null;
13139
+ }
13140
+ } catch (error) {
13141
+ log.warn("hooks.fetch-tracker", `Key normalization failed: ${error instanceof Error ? error.message : "unknown"}`);
13142
+ return null;
13143
+ }
13144
+ }
13145
+ function clearSession(sessionID) {
13146
+ sessionCallCounts.delete(sessionID);
13147
+ sessionCaches.delete(sessionID);
13148
+ }
13149
+ function getOrCreateCounts(sessionID) {
13150
+ let counts = sessionCallCounts.get(sessionID);
13151
+ if (!counts) {
13152
+ counts = new Map;
13153
+ sessionCallCounts.set(sessionID, counts);
13154
+ }
13155
+ return counts;
13156
+ }
13157
+ function getOrCreateCache(sessionID) {
13158
+ let cache = sessionCaches.get(sessionID);
13159
+ if (!cache) {
13160
+ cache = new LRUCache(config.fetch.cacheMaxEntries);
13161
+ sessionCaches.set(sessionID, cache);
13162
+ }
13163
+ return cache;
13164
+ }
13165
+ function incrementCount(sessionID, key) {
13166
+ const counts = getOrCreateCounts(sessionID);
13167
+ const current = counts.get(key) ?? 0;
13168
+ const next = current + 1;
13169
+ counts.set(key, next);
13170
+ return next;
13171
+ }
13172
+ function isCacheExpired(entry) {
13173
+ return Date.now() - entry.timestamp > config.fetch.cacheTtlMs;
13174
+ }
13175
+ function createFetchTrackerHook(_ctx) {
13176
+ return {
13177
+ "tool.execute.after": async (input, output) => {
13178
+ try {
13179
+ if (!FETCH_TOOLS.has(input.tool))
13180
+ return;
13181
+ const key = normalizeKey(input.tool, input.args);
13182
+ if (!key)
13183
+ return;
13184
+ const count = incrementCount(input.sessionID, key);
13185
+ if (count > config.fetch.maxCallsPerResource) {
13186
+ output.output = `<fetch-blocked>This resource has been fetched ${count} times this session. The content is already available in the conversation above. Use the information already available instead of re-fetching.</fetch-blocked>`;
13187
+ return;
13188
+ }
13189
+ const cache = getOrCreateCache(input.sessionID);
13190
+ const cached = cache.get(key);
13191
+ if (count > 1 && cached && !isCacheExpired(cached)) {
13192
+ let cachedOutput = `<from-cache>Returning cached result (fetched ${count} time${count !== 1 ? "s" : ""} previously).</from-cache>
13193
+
13194
+ ${cached.content}`;
13195
+ if (count >= config.fetch.warnThreshold) {
13196
+ cachedOutput += `
13197
+
13198
+ <fetch-warning>You have fetched this resource ${count} times. The content is cached and identical. Consider using the information you already have instead of re-fetching.</fetch-warning>`;
13199
+ }
13200
+ output.output = cachedOutput;
13201
+ } else {
13202
+ if (output.output) {
13203
+ cache.set(key, { content: output.output, timestamp: Date.now() });
13204
+ }
13205
+ }
13206
+ } catch (error) {
13207
+ log.warn("hooks.fetch-tracker", `After hook error: ${error instanceof Error ? error.message : "unknown"}`);
13208
+ }
13209
+ },
13210
+ event: async ({ event }) => {
13211
+ if (event.type === "session.deleted") {
13212
+ const props = event.properties;
13213
+ if (props?.info?.id) {
13214
+ clearSession(props.info.id);
13215
+ }
13216
+ }
13217
+ },
13218
+ cleanupSession: clearSession
13219
+ };
13220
+ }
13221
+
12207
13222
  // src/hooks/file-ops-tracker.ts
12208
13223
  var sessionFileOps = new Map;
12209
13224
  function getOrCreateOps(sessionID) {
@@ -12494,7 +13509,7 @@ __export(exports_external, {
12494
13509
  pipe: () => pipe2,
12495
13510
  partialRecord: () => partialRecord,
12496
13511
  parseAsync: () => parseAsync2,
12497
- parse: () => parse4,
13512
+ parse: () => parse6,
12498
13513
  overwrite: () => _overwrite,
12499
13514
  optional: () => optional2,
12500
13515
  object: () => object2,
@@ -12684,7 +13699,7 @@ __export(exports_core2, {
12684
13699
  regexes: () => exports_regexes,
12685
13700
  prettifyError: () => prettifyError,
12686
13701
  parseAsync: () => parseAsync,
12687
- parse: () => parse2,
13702
+ parse: () => parse4,
12688
13703
  locales: () => exports_locales,
12689
13704
  isValidJWT: () => isValidJWT,
12690
13705
  isValidBase64URL: () => isValidBase64URL,
@@ -13783,7 +14798,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
13783
14798
  }
13784
14799
  return result.value;
13785
14800
  };
13786
- var parse2 = /* @__PURE__ */ _parse($ZodRealError);
14801
+ var parse4 = /* @__PURE__ */ _parse($ZodRealError);
13787
14802
  var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
13788
14803
  const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };
13789
14804
  let result = schema._zod.run({ value, issues: [] }, ctx);
@@ -16301,10 +17316,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
16301
17316
  throw new Error("implement() must be called with a function");
16302
17317
  }
16303
17318
  return function(...args) {
16304
- const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
17319
+ const parsedArgs = inst._def.input ? parse4(inst._def.input, args) : args;
16305
17320
  const result = Reflect.apply(func, this, parsedArgs);
16306
17321
  if (inst._def.output) {
16307
- return parse2(inst._def.output, result);
17322
+ return parse4(inst._def.output, result);
16308
17323
  }
16309
17324
  return result;
16310
17325
  };
@@ -22813,13 +23828,13 @@ function _stringbool(Classes, _params) {
22813
23828
  });
22814
23829
  return codec;
22815
23830
  }
22816
- function _stringFormat(Class2, format, fnOrRegex, _params = {}) {
23831
+ function _stringFormat(Class2, format2, fnOrRegex, _params = {}) {
22817
23832
  const params = normalizeParams(_params);
22818
23833
  const def = {
22819
23834
  ...normalizeParams(_params),
22820
23835
  check: "string_format",
22821
23836
  type: "string",
22822
- format,
23837
+ format: format2,
22823
23838
  fn: typeof fnOrRegex === "function" ? fnOrRegex : (val) => fnOrRegex.test(val),
22824
23839
  ...params
22825
23840
  };
@@ -22881,13 +23896,13 @@ class JSONSchemaGenerator {
22881
23896
  case "string": {
22882
23897
  const json = _json;
22883
23898
  json.type = "string";
22884
- const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
23899
+ const { minimum, maximum, format: format2, patterns, contentEncoding } = schema._zod.bag;
22885
23900
  if (typeof minimum === "number")
22886
23901
  json.minLength = minimum;
22887
23902
  if (typeof maximum === "number")
22888
23903
  json.maxLength = maximum;
22889
- if (format) {
22890
- json.format = formatMap[format] ?? format;
23904
+ if (format2) {
23905
+ json.format = formatMap[format2] ?? format2;
22891
23906
  if (json.format === "")
22892
23907
  delete json.format;
22893
23908
  }
@@ -22910,8 +23925,8 @@ class JSONSchemaGenerator {
22910
23925
  }
22911
23926
  case "number": {
22912
23927
  const json = _json;
22913
- const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
22914
- if (typeof format === "string" && format.includes("int"))
23928
+ const { minimum, maximum, format: format2, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
23929
+ if (typeof format2 === "string" && format2.includes("int"))
22915
23930
  json.type = "integer";
22916
23931
  else
22917
23932
  json.type = "number";
@@ -23712,7 +24727,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
23712
24727
  });
23713
24728
 
23714
24729
  // node_modules/zod/v4/classic/parse.js
23715
- var parse4 = /* @__PURE__ */ _parse(ZodRealError);
24730
+ var parse6 = /* @__PURE__ */ _parse(ZodRealError);
23716
24731
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
23717
24732
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
23718
24733
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -23746,7 +24761,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
23746
24761
  reg.add(inst, meta);
23747
24762
  return inst;
23748
24763
  };
23749
- inst.parse = (data, params) => parse4(inst, data, params, { callee: inst.parse });
24764
+ inst.parse = (data, params) => parse6(inst, data, params, { callee: inst.parse });
23750
24765
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
23751
24766
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
23752
24767
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -24011,8 +25026,8 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
24011
25026
  $ZodCustomStringFormat.init(inst, def);
24012
25027
  ZodStringFormat.init(inst, def);
24013
25028
  });
24014
- function stringFormat(format, fnOrRegex, _params = {}) {
24015
- return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
25029
+ function stringFormat(format2, fnOrRegex, _params = {}) {
25030
+ return _stringFormat(ZodCustomStringFormat, format2, fnOrRegex, _params);
24016
25031
  }
24017
25032
  function hostname2(_params) {
24018
25033
  return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
@@ -24022,11 +25037,11 @@ function hex2(_params) {
24022
25037
  }
24023
25038
  function hash(alg, params) {
24024
25039
  const enc = params?.enc ?? "hex";
24025
- const format = `${alg}_${enc}`;
24026
- const regex = exports_regexes[format];
25040
+ const format2 = `${alg}_${enc}`;
25041
+ const regex = exports_regexes[format2];
24027
25042
  if (!regex)
24028
- throw new Error(`Unrecognized hash format: ${format}`);
24029
- return _stringFormat(ZodCustomStringFormat, format, regex, params);
25043
+ throw new Error(`Unrecognized hash format: ${format2}`);
25044
+ return _stringFormat(ZodCustomStringFormat, format2, regex, params);
24030
25045
  }
24031
25046
  var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
24032
25047
  $ZodNumber.init(inst, def);
@@ -24801,13 +25816,13 @@ Returns relevant code examples and patterns to follow.`,
24801
25816
  if (categories.length === 0) {
24802
25817
  return "No specific patterns found for this task. Proceed using general best practices.";
24803
25818
  }
24804
- log.info("mindmodel", `Matched categories: ${categories.join(", ")}`);
25819
+ log.debug("mindmodel", `Matched categories: ${categories.join(", ")}`);
24805
25820
  const examples = await loadExamples(mindmodel, categories);
24806
25821
  if (examples.length === 0) {
24807
25822
  return "Categories matched but no examples found. Proceed using general best practices.";
24808
25823
  }
24809
25824
  const formatted = formatExamplesForInjection(examples);
24810
- log.info("mindmodel", `Returning ${examples.length} examples`);
25825
+ log.debug("mindmodel", `Returning ${examples.length} examples`);
24811
25826
  return formatted;
24812
25827
  } catch (error45) {
24813
25828
  log.warn("mindmodel", `Lookup failed: ${error45 instanceof Error ? error45.message : "unknown"}`);
@@ -24829,7 +25844,7 @@ function hashTask(task) {
24829
25844
  return hash2.toString(36);
24830
25845
  }
24831
25846
 
24832
- class LRUCache {
25847
+ class LRUCache2 {
24833
25848
  maxSize;
24834
25849
  cache = new Map;
24835
25850
  constructor(maxSize) {
@@ -24861,7 +25876,7 @@ function createMindmodelInjectorHook(ctx) {
24861
25876
  let cachedMindmodel2;
24862
25877
  let cachedSystemMd;
24863
25878
  let pendingInjection = null;
24864
- const matchedTasks = new LRUCache(2000);
25879
+ const matchedTasks = new LRUCache2(2000);
24865
25880
  async function getMindmodel2() {
24866
25881
  if (cachedMindmodel2 === undefined) {
24867
25882
  cachedMindmodel2 = await loadMindmodel(ctx.directory);
@@ -27956,7 +28971,7 @@ function createSessionStore(options = {}) {
27956
28971
  return store;
27957
28972
  }
27958
28973
  // src/octto/state/persistence.ts
27959
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmSync } from "fs";
28974
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, rmSync } from "fs";
27960
28975
  import { join as join10 } from "path";
27961
28976
  function validateSessionId(sessionId) {
27962
28977
  if (!/^[a-zA-Z0-9_-]+$/.test(sessionId)) {
@@ -27969,7 +28984,7 @@ function createStatePersistence(baseDir = STATE_DIR) {
27969
28984
  return join10(baseDir, `${sessionId}.json`);
27970
28985
  }
27971
28986
  function ensureDir() {
27972
- if (!existsSync2(baseDir)) {
28987
+ if (!existsSync3(baseDir)) {
27973
28988
  mkdirSync2(baseDir, { recursive: true });
27974
28989
  }
27975
28990
  }
@@ -27982,7 +28997,7 @@ function createStatePersistence(baseDir = STATE_DIR) {
27982
28997
  },
27983
28998
  async load(sessionId) {
27984
28999
  const filePath = getFilePath(sessionId);
27985
- if (!existsSync2(filePath)) {
29000
+ if (!existsSync3(filePath)) {
27986
29001
  return null;
27987
29002
  }
27988
29003
  const content = await Bun.file(filePath).text();
@@ -27990,12 +29005,12 @@ function createStatePersistence(baseDir = STATE_DIR) {
27990
29005
  },
27991
29006
  async delete(sessionId) {
27992
29007
  const filePath = getFilePath(sessionId);
27993
- if (existsSync2(filePath)) {
29008
+ if (existsSync3(filePath)) {
27994
29009
  rmSync(filePath);
27995
29010
  }
27996
29011
  },
27997
29012
  async list() {
27998
- if (!existsSync2(baseDir)) {
29013
+ if (!existsSync3(baseDir)) {
27999
29014
  return [];
28000
29015
  }
28001
29016
  const files = readdirSync(baseDir);
@@ -29248,9 +30263,6 @@ function createOcttoTools(sessions, client, tracker) {
29248
30263
  };
29249
30264
  }
29250
30265
 
29251
- // src/tools/pty/manager.ts
29252
- import { spawn as spawn3 } from "bun-pty";
29253
-
29254
30266
  // src/tools/pty/buffer.ts
29255
30267
  var parsed = parseInt(process.env.PTY_MAX_BUFFER_LINES || "50000", 10);
29256
30268
  var DEFAULT_MAX_LINES = isNaN(parsed) ? 50000 : parsed;
@@ -29293,7 +30305,6 @@ class RingBuffer {
29293
30305
  this.lines = [];
29294
30306
  }
29295
30307
  }
29296
-
29297
30308
  // src/tools/pty/manager.ts
29298
30309
  function generateId2() {
29299
30310
  const hex3 = Array.from(crypto.getRandomValues(new Uint8Array(4))).map((b) => b.toString(16).padStart(2, "0")).join("");
@@ -29302,7 +30313,19 @@ function generateId2() {
29302
30313
 
29303
30314
  class PTYManager {
29304
30315
  sessions = new Map;
30316
+ spawnFn = null;
30317
+ _available = false;
30318
+ init(spawnFn) {
30319
+ this.spawnFn = spawnFn;
30320
+ this._available = true;
30321
+ }
30322
+ get available() {
30323
+ return this._available;
30324
+ }
29305
30325
  spawn(opts) {
30326
+ if (!this.spawnFn) {
30327
+ throw new Error("PTY unavailable: bun-pty native library could not be loaded. " + "Set BUN_PTY_LIB environment variable to the path of the native library " + "(e.g., .opencode/node_modules/bun-pty/rust-pty/target/release/librust_pty.dylib)");
30328
+ }
29306
30329
  const id = generateId2();
29307
30330
  const args = opts.args ?? [];
29308
30331
  const workdir = opts.workdir ?? process.cwd();
@@ -29310,7 +30333,7 @@ class PTYManager {
29310
30333
  const title = opts.title ?? (`${opts.command} ${args.join(" ")}`.trim() || `Terminal ${id.slice(-4)}`);
29311
30334
  let ptyProcess;
29312
30335
  try {
29313
- ptyProcess = spawn3(opts.command, args, {
30336
+ ptyProcess = this.spawnFn(opts.command, args, {
29314
30337
  name: "xterm-256color",
29315
30338
  cols: 120,
29316
30339
  rows: 40,
@@ -29432,150 +30455,166 @@ class PTYManager {
29432
30455
  };
29433
30456
  }
29434
30457
  }
29435
- // src/tools/pty/tools/spawn.ts
29436
- var DESCRIPTION = `Spawns a new interactive PTY (pseudo-terminal) session that runs in the background.
29437
-
29438
- Unlike the built-in bash tool which runs commands synchronously and waits for completion, PTY sessions persist and allow you to:
29439
- - Run long-running processes (dev servers, watch modes, etc.)
29440
- - Send interactive input (including Ctrl+C, arrow keys, etc.)
29441
- - Read output at any time
29442
- - Manage multiple concurrent terminal sessions
29443
-
29444
- Usage:
29445
- - The \`command\` parameter is required (e.g., "npm", "python", "bash")
29446
- - Use \`args\` to pass arguments to the command (e.g., ["run", "dev"])
29447
- - Use \`workdir\` to set the working directory (defaults to project root)
29448
- - Use \`env\` to set additional environment variables
29449
- - Use \`title\` to give the session a human-readable name
29450
- - Use \`description\` for a clear, concise 5-10 word description (optional)
29451
-
29452
- Returns the session info including:
29453
- - \`id\`: Unique identifier (pty_XXXXXXXX) for use with other pty_* tools
29454
- - \`pid\`: Process ID
29455
- - \`status\`: Current status ("running")
29456
-
29457
- After spawning, use:
29458
- - \`pty_write\` to send input to the PTY
29459
- - \`pty_read\` to read output from the PTY
29460
- - \`pty_list\` to see all active PTY sessions
29461
- - \`pty_kill\` to terminate the PTY
29462
-
29463
- Examples:
29464
- - Start a dev server: command="npm", args=["run", "dev"], title="Dev Server"
29465
- - Start a Python REPL: command="python3", title="Python REPL"
29466
- - Run tests in watch mode: command="npm", args=["test", "--", "--watch"]`;
29467
- function createPtySpawnTool(manager) {
29468
- return tool({
29469
- description: DESCRIPTION,
29470
- args: {
29471
- command: tool.schema.string().describe("The command/executable to run"),
29472
- args: tool.schema.array(tool.schema.string()).optional().describe("Arguments to pass to the command"),
29473
- workdir: tool.schema.string().optional().describe("Working directory for the PTY session"),
29474
- env: tool.schema.record(tool.schema.string(), tool.schema.string()).optional().describe("Additional environment variables"),
29475
- title: tool.schema.string().optional().describe("Human-readable title for the session"),
29476
- description: tool.schema.string().optional().describe("Clear, concise description of what this PTY session is for in 5-10 words")
29477
- },
29478
- execute: async (args, ctx) => {
29479
- const info = manager.spawn({
29480
- command: args.command,
29481
- args: args.args,
29482
- workdir: args.workdir,
29483
- env: args.env,
29484
- title: args.title,
29485
- parentSessionId: ctx.sessionID
29486
- });
29487
- const output = [
29488
- `<pty_spawned>`,
29489
- `ID: ${info.id}`,
29490
- `Title: ${info.title}`,
29491
- `Command: ${info.command} ${info.args.join(" ")}`,
29492
- `Workdir: ${info.workdir}`,
29493
- `PID: ${info.pid}`,
29494
- `Status: ${info.status}`,
29495
- `</pty_spawned>`
29496
- ].join(`
29497
- `);
29498
- return output;
30458
+ // src/tools/pty/pty-loader.ts
30459
+ import { existsSync as existsSync4 } from "fs";
30460
+ import { dirname as dirname3, join as join11 } from "path";
30461
+ var cachedModule = null;
30462
+ var loadAttempted = false;
30463
+ var loadError = null;
30464
+ function probeBunPtyLib() {
30465
+ if (process.env.BUN_PTY_LIB)
30466
+ return;
30467
+ const platform = process.platform;
30468
+ const arch = process.arch;
30469
+ const filenames = platform === "darwin" ? arch === "arm64" ? ["librust_pty_arm64.dylib", "librust_pty.dylib"] : ["librust_pty.dylib"] : platform === "win32" ? ["rust_pty.dll"] : arch === "arm64" ? ["librust_pty_arm64.so", "librust_pty.so"] : ["librust_pty.so"];
30470
+ const cwd = process.cwd();
30471
+ const additionalBasePaths = [
30472
+ join11(cwd, ".opencode", "node_modules", "bun-pty", "rust-pty", "target", "release"),
30473
+ join11(cwd, ".micode", "node_modules", "bun-pty", "rust-pty", "target", "release")
30474
+ ];
30475
+ try {
30476
+ const bunPtyMain = __require.resolve("bun-pty");
30477
+ if (bunPtyMain) {
30478
+ const pkgDir = dirname3(dirname3(bunPtyMain));
30479
+ additionalBasePaths.unshift(join11(pkgDir, "rust-pty", "target", "release"));
29499
30480
  }
29500
- });
30481
+ } catch {}
30482
+ for (const basePath of additionalBasePaths) {
30483
+ for (const filename of filenames) {
30484
+ const candidate = join11(basePath, filename);
30485
+ if (existsSync4(candidate)) {
30486
+ process.env.BUN_PTY_LIB = candidate;
30487
+ log.info("pty.loader", `Auto-resolved BUN_PTY_LIB=${candidate}`);
30488
+ return;
30489
+ }
30490
+ }
30491
+ }
29501
30492
  }
29502
- // src/tools/pty/tools/write.ts
29503
- var DESCRIPTION2 = `Sends input data to an active PTY session.
30493
+ async function loadBunPty() {
30494
+ if (loadAttempted)
30495
+ return cachedModule;
30496
+ loadAttempted = true;
30497
+ probeBunPtyLib();
30498
+ try {
30499
+ cachedModule = await import("bun-pty");
30500
+ log.info("pty.loader", "bun-pty loaded successfully");
30501
+ return cachedModule;
30502
+ } catch (error45) {
30503
+ loadError = error45 instanceof Error ? error45.message : String(error45);
30504
+ const firstLine = loadError.split(`
30505
+ `)[0];
30506
+ log.warn("pty.loader", `bun-pty unavailable: ${firstLine}`);
30507
+ log.warn("pty.loader", "PTY tools will be disabled. Set BUN_PTY_LIB env var to the native library path to fix.");
30508
+ cachedModule = null;
30509
+ return null;
30510
+ }
30511
+ }
30512
+ // src/tools/pty/tools/kill.ts
30513
+ var DESCRIPTION = `Terminates a PTY session and optionally cleans up its buffer.
29504
30514
 
29505
30515
  Use this tool to:
29506
- - Type commands or text into an interactive terminal
29507
- - Send special key sequences (Ctrl+C, Enter, arrow keys, etc.)
29508
- - Respond to prompts in interactive programs
30516
+ - Stop a running process (sends SIGTERM)
30517
+ - Clean up an exited session to free memory
30518
+ - Remove a session from the list
29509
30519
 
29510
30520
  Usage:
29511
30521
  - \`id\`: The PTY session ID (from pty_spawn or pty_list)
29512
- - \`data\`: The input to send (text, commands, or escape sequences)
30522
+ - \`cleanup\`: If true, removes the session and frees the buffer (default: false)
29513
30523
 
29514
- Common escape sequences:
29515
- - Enter/newline: "\\n" or "\\r"
29516
- - Ctrl+C (interrupt): "\\x03"
29517
- - Ctrl+D (EOF): "\\x04"
29518
- - Ctrl+Z (suspend): "\\x1a"
29519
- - Tab: "\\t"
29520
- - Arrow Up: "\\x1b[A"
29521
- - Arrow Down: "\\x1b[B"
29522
- - Arrow Right: "\\x1b[C"
29523
- - Arrow Left: "\\x1b[D"
30524
+ Behavior:
30525
+ - If the session is running, it will be killed (status becomes "killed")
30526
+ - If cleanup=false (default), the session remains in the list with its output buffer intact
30527
+ - If cleanup=true, the session is removed entirely and the buffer is freed
30528
+ - Keeping sessions without cleanup allows you to compare logs between runs
29524
30529
 
29525
- Returns success or error message.
30530
+ Tips:
30531
+ - Use cleanup=false if you might want to read the output later
30532
+ - Use cleanup=true when you're done with the session entirely
30533
+ - To send Ctrl+C instead of killing, use pty_write with data="\\x03"
29526
30534
 
29527
30535
  Examples:
29528
- - Send a command: data="ls -la\\n"
29529
- - Interrupt a process: data="\\x03"
29530
- - Answer a prompt: data="yes\\n"`;
29531
- function parseEscapeSequences(input) {
29532
- return input.replace(/\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[nrt0\\])/g, (match, seq) => {
29533
- if (seq.startsWith("x")) {
29534
- return String.fromCharCode(parseInt(seq.slice(1), 16));
29535
- }
29536
- if (seq.startsWith("u")) {
29537
- return String.fromCharCode(parseInt(seq.slice(1), 16));
29538
- }
29539
- switch (seq) {
29540
- case "n":
29541
- return `
29542
- `;
29543
- case "r":
29544
- return "\r";
29545
- case "t":
29546
- return "\t";
29547
- case "0":
29548
- return "\x00";
29549
- case "\\":
29550
- return "\\";
29551
- default:
29552
- return match;
30536
+ - Kill but keep logs: cleanup=false (or omit)
30537
+ - Kill and remove: cleanup=true`;
30538
+ function createPtyKillTool(manager) {
30539
+ return tool({
30540
+ description: DESCRIPTION,
30541
+ args: {
30542
+ id: tool.schema.string().describe("The PTY session ID (e.g., pty_a1b2c3d4)"),
30543
+ cleanup: tool.schema.boolean().optional().describe("If true, removes the session and frees the buffer (default: false)")
30544
+ },
30545
+ execute: async (args) => {
30546
+ const session = manager.get(args.id);
30547
+ if (!session) {
30548
+ throw new Error(`PTY session '${args.id}' not found. Use pty_list to see active sessions.`);
30549
+ }
30550
+ const wasRunning = session.status === "running";
30551
+ const cleanup = args.cleanup ?? false;
30552
+ const success2 = manager.kill(args.id, cleanup);
30553
+ if (!success2) {
30554
+ throw new Error(`Failed to kill PTY session '${args.id}'.`);
30555
+ }
30556
+ const action = wasRunning ? "Killed" : "Cleaned up";
30557
+ const cleanupNote = cleanup ? " (session removed)" : " (session retained for log access)";
30558
+ return [
30559
+ `<pty_killed>`,
30560
+ `${action}: ${args.id}${cleanupNote}`,
30561
+ `Title: ${session.title}`,
30562
+ `Command: ${session.command} ${session.args.join(" ")}`,
30563
+ `Final line count: ${session.lineCount}`,
30564
+ `</pty_killed>`
30565
+ ].join(`
30566
+ `);
29553
30567
  }
29554
30568
  });
29555
30569
  }
29556
- function createPtyWriteTool(manager) {
30570
+ // src/tools/pty/tools/list.ts
30571
+ var DESCRIPTION2 = `Lists all PTY sessions (active and exited).
30572
+
30573
+ Use this tool to:
30574
+ - See all running and exited PTY sessions
30575
+ - Get session IDs for use with other pty_* tools
30576
+ - Check the status and output line count of each session
30577
+ - Monitor which processes are still running
30578
+
30579
+ Returns for each session:
30580
+ - \`id\`: Unique identifier for use with other tools
30581
+ - \`title\`: Human-readable name
30582
+ - \`command\`: The command that was executed
30583
+ - \`status\`: Current status (running, exited, killed)
30584
+ - \`exitCode\`: Exit code (if exited/killed)
30585
+ - \`pid\`: Process ID
30586
+ - \`lineCount\`: Number of lines in the output buffer
30587
+ - \`createdAt\`: When the session was created
30588
+
30589
+ Tips:
30590
+ - Use the session ID with pty_read, pty_write, or pty_kill
30591
+ - Sessions remain in the list after exit until explicitly cleaned up with pty_kill
30592
+ - This allows you to compare output from multiple sessions`;
30593
+ function createPtyListTool(manager) {
29557
30594
  return tool({
29558
30595
  description: DESCRIPTION2,
29559
- args: {
29560
- id: tool.schema.string().describe("The PTY session ID (e.g., pty_a1b2c3d4)"),
29561
- data: tool.schema.string().describe("The input data to send to the PTY")
29562
- },
29563
- execute: async (args) => {
29564
- const session = manager.get(args.id);
29565
- if (!session) {
29566
- throw new Error(`PTY session '${args.id}' not found. Use pty_list to see active sessions.`);
29567
- }
29568
- if (session.status !== "running") {
29569
- throw new Error(`Cannot write to PTY '${args.id}' - session status is '${session.status}'.`);
30596
+ args: {},
30597
+ execute: async () => {
30598
+ const sessions = manager.list();
30599
+ if (sessions.length === 0) {
30600
+ return `<pty_list>
30601
+ No active PTY sessions.
30602
+ </pty_list>`;
29570
30603
  }
29571
- const parsedData = parseEscapeSequences(args.data);
29572
- const success2 = manager.write(args.id, parsedData);
29573
- if (!success2) {
29574
- throw new Error(`Failed to write to PTY '${args.id}'.`);
30604
+ const lines = ["<pty_list>"];
30605
+ for (const session of sessions) {
30606
+ const exitInfo = session.exitCode !== undefined ? ` (exit: ${session.exitCode})` : "";
30607
+ lines.push(`[${session.id}] ${session.title}`);
30608
+ lines.push(` Command: ${session.command} ${session.args.join(" ")}`);
30609
+ lines.push(` Status: ${session.status}${exitInfo}`);
30610
+ lines.push(` PID: ${session.pid} | Lines: ${session.lineCount} | Workdir: ${session.workdir}`);
30611
+ lines.push(` Created: ${session.createdAt.toISOString()}`);
30612
+ lines.push("");
29575
30613
  }
29576
- const preview = args.data.length > 50 ? `${args.data.slice(0, 50)}...` : args.data;
29577
- const displayPreview = preview.replace(/\x03/g, "^C").replace(/\x04/g, "^D").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
29578
- return `Sent ${parsedData.length} bytes to ${args.id}: "${displayPreview}"`;
30614
+ lines.push(`Total: ${sessions.length} session(s)`);
30615
+ lines.push("</pty_list>");
30616
+ return lines.join(`
30617
+ `);
29579
30618
  }
29580
30619
  });
29581
30620
  }
@@ -29709,112 +30748,150 @@ function createPtyReadTool(manager) {
29709
30748
  }
29710
30749
  });
29711
30750
  }
29712
- // src/tools/pty/tools/list.ts
29713
- var DESCRIPTION4 = `Lists all PTY sessions (active and exited).
30751
+ // src/tools/pty/tools/spawn.ts
30752
+ var DESCRIPTION4 = `Spawns a new interactive PTY (pseudo-terminal) session that runs in the background.
29714
30753
 
29715
- Use this tool to:
29716
- - See all running and exited PTY sessions
29717
- - Get session IDs for use with other pty_* tools
29718
- - Check the status and output line count of each session
29719
- - Monitor which processes are still running
30754
+ Unlike the built-in bash tool which runs commands synchronously and waits for completion, PTY sessions persist and allow you to:
30755
+ - Run long-running processes (dev servers, watch modes, etc.)
30756
+ - Send interactive input (including Ctrl+C, arrow keys, etc.)
30757
+ - Read output at any time
30758
+ - Manage multiple concurrent terminal sessions
29720
30759
 
29721
- Returns for each session:
29722
- - \`id\`: Unique identifier for use with other tools
29723
- - \`title\`: Human-readable name
29724
- - \`command\`: The command that was executed
29725
- - \`status\`: Current status (running, exited, killed)
29726
- - \`exitCode\`: Exit code (if exited/killed)
30760
+ Usage:
30761
+ - The \`command\` parameter is required (e.g., "npm", "python", "bash")
30762
+ - Use \`args\` to pass arguments to the command (e.g., ["run", "dev"])
30763
+ - Use \`workdir\` to set the working directory (defaults to project root)
30764
+ - Use \`env\` to set additional environment variables
30765
+ - Use \`title\` to give the session a human-readable name
30766
+ - Use \`description\` for a clear, concise 5-10 word description (optional)
30767
+
30768
+ Returns the session info including:
30769
+ - \`id\`: Unique identifier (pty_XXXXXXXX) for use with other pty_* tools
29727
30770
  - \`pid\`: Process ID
29728
- - \`lineCount\`: Number of lines in the output buffer
29729
- - \`createdAt\`: When the session was created
30771
+ - \`status\`: Current status ("running")
29730
30772
 
29731
- Tips:
29732
- - Use the session ID with pty_read, pty_write, or pty_kill
29733
- - Sessions remain in the list after exit until explicitly cleaned up with pty_kill
29734
- - This allows you to compare output from multiple sessions`;
29735
- function createPtyListTool(manager) {
30773
+ After spawning, use:
30774
+ - \`pty_write\` to send input to the PTY
30775
+ - \`pty_read\` to read output from the PTY
30776
+ - \`pty_list\` to see all active PTY sessions
30777
+ - \`pty_kill\` to terminate the PTY
30778
+
30779
+ Examples:
30780
+ - Start a dev server: command="npm", args=["run", "dev"], title="Dev Server"
30781
+ - Start a Python REPL: command="python3", title="Python REPL"
30782
+ - Run tests in watch mode: command="npm", args=["test", "--", "--watch"]`;
30783
+ function createPtySpawnTool(manager) {
29736
30784
  return tool({
29737
30785
  description: DESCRIPTION4,
29738
- args: {},
29739
- execute: async () => {
29740
- const sessions = manager.list();
29741
- if (sessions.length === 0) {
29742
- return `<pty_list>
29743
- No active PTY sessions.
29744
- </pty_list>`;
29745
- }
29746
- const lines = ["<pty_list>"];
29747
- for (const session of sessions) {
29748
- const exitInfo = session.exitCode !== undefined ? ` (exit: ${session.exitCode})` : "";
29749
- lines.push(`[${session.id}] ${session.title}`);
29750
- lines.push(` Command: ${session.command} ${session.args.join(" ")}`);
29751
- lines.push(` Status: ${session.status}${exitInfo}`);
29752
- lines.push(` PID: ${session.pid} | Lines: ${session.lineCount} | Workdir: ${session.workdir}`);
29753
- lines.push(` Created: ${session.createdAt.toISOString()}`);
29754
- lines.push("");
29755
- }
29756
- lines.push(`Total: ${sessions.length} session(s)`);
29757
- lines.push("</pty_list>");
29758
- return lines.join(`
30786
+ args: {
30787
+ command: tool.schema.string().describe("The command/executable to run"),
30788
+ args: tool.schema.array(tool.schema.string()).optional().describe("Arguments to pass to the command"),
30789
+ workdir: tool.schema.string().optional().describe("Working directory for the PTY session"),
30790
+ env: tool.schema.record(tool.schema.string(), tool.schema.string()).optional().describe("Additional environment variables"),
30791
+ title: tool.schema.string().optional().describe("Human-readable title for the session"),
30792
+ description: tool.schema.string().optional().describe("Clear, concise description of what this PTY session is for in 5-10 words")
30793
+ },
30794
+ execute: async (args, ctx) => {
30795
+ const info = manager.spawn({
30796
+ command: args.command,
30797
+ args: args.args,
30798
+ workdir: args.workdir,
30799
+ env: args.env,
30800
+ title: args.title,
30801
+ parentSessionId: ctx.sessionID
30802
+ });
30803
+ const output = [
30804
+ `<pty_spawned>`,
30805
+ `ID: ${info.id}`,
30806
+ `Title: ${info.title}`,
30807
+ `Command: ${info.command} ${info.args.join(" ")}`,
30808
+ `Workdir: ${info.workdir}`,
30809
+ `PID: ${info.pid}`,
30810
+ `Status: ${info.status}`,
30811
+ `</pty_spawned>`
30812
+ ].join(`
29759
30813
  `);
30814
+ return output;
29760
30815
  }
29761
30816
  });
29762
30817
  }
29763
- // src/tools/pty/tools/kill.ts
29764
- var DESCRIPTION5 = `Terminates a PTY session and optionally cleans up its buffer.
30818
+ // src/tools/pty/tools/write.ts
30819
+ var DESCRIPTION5 = `Sends input data to an active PTY session.
29765
30820
 
29766
30821
  Use this tool to:
29767
- - Stop a running process (sends SIGTERM)
29768
- - Clean up an exited session to free memory
29769
- - Remove a session from the list
30822
+ - Type commands or text into an interactive terminal
30823
+ - Send special key sequences (Ctrl+C, Enter, arrow keys, etc.)
30824
+ - Respond to prompts in interactive programs
29770
30825
 
29771
30826
  Usage:
29772
30827
  - \`id\`: The PTY session ID (from pty_spawn or pty_list)
29773
- - \`cleanup\`: If true, removes the session and frees the buffer (default: false)
30828
+ - \`data\`: The input to send (text, commands, or escape sequences)
29774
30829
 
29775
- Behavior:
29776
- - If the session is running, it will be killed (status becomes "killed")
29777
- - If cleanup=false (default), the session remains in the list with its output buffer intact
29778
- - If cleanup=true, the session is removed entirely and the buffer is freed
29779
- - Keeping sessions without cleanup allows you to compare logs between runs
30830
+ Common escape sequences:
30831
+ - Enter/newline: "\\n" or "\\r"
30832
+ - Ctrl+C (interrupt): "\\x03"
30833
+ - Ctrl+D (EOF): "\\x04"
30834
+ - Ctrl+Z (suspend): "\\x1a"
30835
+ - Tab: "\\t"
30836
+ - Arrow Up: "\\x1b[A"
30837
+ - Arrow Down: "\\x1b[B"
30838
+ - Arrow Right: "\\x1b[C"
30839
+ - Arrow Left: "\\x1b[D"
29780
30840
 
29781
- Tips:
29782
- - Use cleanup=false if you might want to read the output later
29783
- - Use cleanup=true when you're done with the session entirely
29784
- - To send Ctrl+C instead of killing, use pty_write with data="\\x03"
30841
+ Returns success or error message.
29785
30842
 
29786
30843
  Examples:
29787
- - Kill but keep logs: cleanup=false (or omit)
29788
- - Kill and remove: cleanup=true`;
29789
- function createPtyKillTool(manager) {
30844
+ - Send a command: data="ls -la\\n"
30845
+ - Interrupt a process: data="\\x03"
30846
+ - Answer a prompt: data="yes\\n"`;
30847
+ function parseEscapeSequences(input) {
30848
+ return input.replace(/\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[nrt0\\])/g, (match, seq) => {
30849
+ if (seq.startsWith("x")) {
30850
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
30851
+ }
30852
+ if (seq.startsWith("u")) {
30853
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
30854
+ }
30855
+ switch (seq) {
30856
+ case "n":
30857
+ return `
30858
+ `;
30859
+ case "r":
30860
+ return "\r";
30861
+ case "t":
30862
+ return "\t";
30863
+ case "0":
30864
+ return "\x00";
30865
+ case "\\":
30866
+ return "\\";
30867
+ default:
30868
+ return match;
30869
+ }
30870
+ });
30871
+ }
30872
+ function createPtyWriteTool(manager) {
29790
30873
  return tool({
29791
30874
  description: DESCRIPTION5,
29792
30875
  args: {
29793
30876
  id: tool.schema.string().describe("The PTY session ID (e.g., pty_a1b2c3d4)"),
29794
- cleanup: tool.schema.boolean().optional().describe("If true, removes the session and frees the buffer (default: false)")
30877
+ data: tool.schema.string().describe("The input data to send to the PTY")
29795
30878
  },
29796
30879
  execute: async (args) => {
29797
30880
  const session = manager.get(args.id);
29798
30881
  if (!session) {
29799
30882
  throw new Error(`PTY session '${args.id}' not found. Use pty_list to see active sessions.`);
29800
30883
  }
29801
- const wasRunning = session.status === "running";
29802
- const cleanup = args.cleanup ?? false;
29803
- const success2 = manager.kill(args.id, cleanup);
30884
+ if (session.status !== "running") {
30885
+ throw new Error(`Cannot write to PTY '${args.id}' - session status is '${session.status}'.`);
30886
+ }
30887
+ const parsedData = parseEscapeSequences(args.data);
30888
+ const success2 = manager.write(args.id, parsedData);
29804
30889
  if (!success2) {
29805
- throw new Error(`Failed to kill PTY session '${args.id}'.`);
30890
+ throw new Error(`Failed to write to PTY '${args.id}'.`);
29806
30891
  }
29807
- const action = wasRunning ? "Killed" : "Cleaned up";
29808
- const cleanupNote = cleanup ? " (session removed)" : " (session retained for log access)";
29809
- return [
29810
- `<pty_killed>`,
29811
- `${action}: ${args.id}${cleanupNote}`,
29812
- `Title: ${session.title}`,
29813
- `Command: ${session.command} ${session.args.join(" ")}`,
29814
- `Final line count: ${session.lineCount}`,
29815
- `</pty_killed>`
29816
- ].join(`
29817
- `);
30892
+ const preview = args.data.length > 50 ? `${args.data.slice(0, 50)}...` : args.data;
30893
+ const displayPreview = preview.replace(/\x03/g, "^C").replace(/\x04/g, "^D").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
30894
+ return `Sent ${parsedData.length} bytes to ${args.id}: "${displayPreview}"`;
29818
30895
  }
29819
30896
  });
29820
30897
  }
@@ -30003,6 +31080,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
30003
31080
  const commentCheckerHook = createCommentCheckerHook(ctx);
30004
31081
  const artifactAutoIndexHook = createArtifactAutoIndexHook(ctx);
30005
31082
  const fileOpsTrackerHook = createFileOpsTrackerHook(ctx);
31083
+ const fetchTrackerHook = createFetchTrackerHook(ctx);
30006
31084
  const fragmentInjectorHook = createFragmentInjectorHook(ctx, userConfig);
30007
31085
  if (userConfig?.fragments) {
30008
31086
  const knownAgentNames = new Set(Object.keys(agents));
@@ -30056,7 +31134,11 @@ var OpenCodeConfigPlugin = async (ctx) => {
30056
31134
  }
30057
31135
  });
30058
31136
  const ptyManager = new PTYManager;
30059
- const ptyTools = createPtyTools(ptyManager);
31137
+ const bunPty = await loadBunPty();
31138
+ if (bunPty) {
31139
+ ptyManager.init(bunPty.spawn);
31140
+ }
31141
+ const ptyTools = ptyManager.available ? createPtyTools(ptyManager) : {};
30060
31142
  const spawn_agent = createSpawnAgentTool(ctx);
30061
31143
  const batch_read = createBatchReadTool(ctx);
30062
31144
  const octtoSessionStore = createSessionStore();
@@ -30097,7 +31179,6 @@ var OpenCodeConfigPlugin = async (ctx) => {
30097
31179
  edit: "allow",
30098
31180
  bash: "allow",
30099
31181
  webfetch: "allow",
30100
- doom_loop: "allow",
30101
31182
  external_directory: "allow"
30102
31183
  };
30103
31184
  const mergedAgents = mergeAgentConfigs(agents, userConfig);
@@ -30153,7 +31234,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
30153
31234
  ...output.options,
30154
31235
  thinking: {
30155
31236
  type: "enabled",
30156
- budget_tokens: 32000
31237
+ budgetTokens: 128000
30157
31238
  }
30158
31239
  };
30159
31240
  }
@@ -30214,6 +31295,7 @@ IMPORTANT:
30214
31295
  await contextInjectorHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
30215
31296
  await artifactAutoIndexHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
30216
31297
  await fileOpsTrackerHook["tool.execute.after"]({ tool: input.tool, sessionID: input.sessionID, args: input.args }, output);
31298
+ await fetchTrackerHook["tool.execute.after"]({ tool: input.tool, sessionID: input.sessionID, args: input.args }, output);
30217
31299
  await constraintReviewerHook["tool.execute.after"]({ tool: input.tool, sessionID: input.sessionID, args: input.args }, output);
30218
31300
  },
30219
31301
  "experimental.chat.messages.transform": async (input, output) => {
@@ -30247,6 +31329,7 @@ IMPORTANT:
30247
31329
  thinkModeState.delete(sessionId);
30248
31330
  ptyManager.cleanupBySession(sessionId);
30249
31331
  constraintReviewerHook.cleanupSession(sessionId);
31332
+ fetchTrackerHook.cleanupSession(sessionId);
30250
31333
  const octtoSessions = octtoSessionsMap.get(sessionId);
30251
31334
  if (octtoSessions) {
30252
31335
  for (const octtoSessionId of octtoSessions) {
@@ -30261,6 +31344,7 @@ IMPORTANT:
30261
31344
  await tokenAwareTruncationHook.event({ event });
30262
31345
  await contextWindowMonitorHook.event({ event });
30263
31346
  await fileOpsTrackerHook.event({ event });
31347
+ await fetchTrackerHook.event({ event });
30264
31348
  }
30265
31349
  };
30266
31350
  };