micode 0.9.1 → 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,1061 @@ 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
- const fallbackModel = finalConfig.model || "plugin default";
10641
- console.warn(`[micode] Model "${userOverride.model}" for agent "${name}" is not available. Using ${fallbackModel}.`);
10642
- const { model: _ignored, ...safeOverrides } = userOverride;
10643
- finalConfig = { ...finalConfig, ...safeOverrides };
10732
+ scanError = 6;
10644
10733
  }
10645
- } else {
10646
- finalConfig = { ...finalConfig, ...userOverride };
10647
10734
  }
10735
+ pos++;
10648
10736
  }
10649
- merged[name] = finalConfig;
10737
+ return result;
10650
10738
  }
10651
- return merged;
10652
- }
10653
-
10654
- // src/hooks/artifact-auto-index.ts
10655
- import { readFileSync as readFileSync3 } from "fs";
10656
-
10657
- // src/tools/artifact-index/index.ts
10658
- import { Database } from "bun:sqlite";
10659
- import { existsSync, mkdirSync, readFileSync as readFileSync2 } from "fs";
10660
- import { homedir as homedir2 } from "os";
10661
- import { dirname, join as join2 } from "path";
10662
- var DEFAULT_DB_DIR = join2(homedir2(), ".config", "opencode", "artifact-index");
10663
- var DB_NAME = "context.db";
10664
-
10665
- class ArtifactIndex {
10666
- db = null;
10667
- dbPath;
10668
- constructor(dbDir = DEFAULT_DB_DIR) {
10669
- 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
+ }
10670
10875
  }
10671
- async initialize() {
10672
- const dir = dirname(this.dbPath);
10673
- if (!existsSync(dir)) {
10674
- mkdirSync(dir, { recursive: true });
10876
+ function isUnknownContentCharacter(code) {
10877
+ if (isWhiteSpace(code) || isLineBreak(code)) {
10878
+ return false;
10675
10879
  }
10676
- this.db = new Database(this.dbPath);
10677
- const schemaPath = join2(dirname(import.meta.path), "schema.sql");
10678
- let schema;
10679
- try {
10680
- schema = readFileSync2(schemaPath, "utf-8");
10681
- } catch {
10682
- 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;
10683
10890
  }
10684
- this.db.exec(schema);
10891
+ return true;
10685
10892
  }
10686
- getInlineSchema() {
10687
- return `
10688
- CREATE TABLE IF NOT EXISTS plans (
10689
- id TEXT PRIMARY KEY,
10690
- title TEXT,
10691
- file_path TEXT UNIQUE NOT NULL,
10692
- overview TEXT,
10693
- approach TEXT,
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,
10694
11612
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10695
11613
  indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
10696
11614
  );
@@ -11026,79 +11944,16 @@ function createArtifactAutoIndexHook(_ctx) {
11026
11944
  await index.indexPlan(record);
11027
11945
  return;
11028
11946
  }
11029
- } catch (e) {
11030
- log.error("artifact-auto-index", `Error indexing ${filePath}`, e);
11031
- }
11032
- }
11033
- };
11034
- }
11035
-
11036
- // src/hooks/auto-compact.ts
11037
- import { mkdir, writeFile } from "fs/promises";
11038
- import { join as join3 } from "path";
11039
-
11040
- // src/utils/config.ts
11041
- var config = {
11042
- compaction: {
11043
- threshold: 0.7,
11044
- cooldownMs: 120000,
11045
- timeoutMs: 120000
11046
- },
11047
- contextWindow: {
11048
- warningThreshold: 0.7,
11049
- criticalThreshold: 0.85,
11050
- warningCooldownMs: 120000
11051
- },
11052
- tokens: {
11053
- charsPerToken: 4,
11054
- defaultContextLimit: 200000,
11055
- defaultMaxOutputTokens: 50000,
11056
- safetyMargin: 0.5,
11057
- preserveHeaderLines: 3
11058
- },
11059
- paths: {
11060
- ledgerDir: "thoughts/ledgers",
11061
- ledgerPrefix: "CONTINUITY_",
11062
- rootContextFiles: ["README.md", "ARCHITECTURE.md", "CODE_STYLE.md"],
11063
- dirContextFiles: ["README.md"],
11064
- planPattern: /thoughts\/shared\/plans\/.*\.md$/,
11065
- ledgerPattern: /thoughts\/ledgers\/CONTINUITY_.*\.md$/,
11066
- mindmodelDir: ".mindmodel",
11067
- mindmodelManifest: "manifest.yaml",
11068
- mindmodelSystem: "system.md"
11069
- },
11070
- timeouts: {
11071
- btcaMs: 120000,
11072
- toastSuccessMs: 3000,
11073
- toastWarningMs: 4000,
11074
- toastErrorMs: 5000
11075
- },
11076
- limits: {
11077
- largeFileBytes: 100 * 1024,
11078
- maxLinesNoExtract: 200,
11079
- ptyMaxBufferLines: 50000,
11080
- ptyDefaultReadLimit: 500,
11081
- ptyMaxLineLength: 2000,
11082
- astGrepMaxMatches: 100,
11083
- contextCacheTtlMs: 30000,
11084
- contextCacheMaxSize: 100
11085
- },
11086
- octto: {
11087
- answerTimeoutMs: 5 * 60 * 1000,
11088
- reviewTimeoutMs: 10 * 60 * 1000,
11089
- maxIterations: 50,
11090
- maxQuestions: 15,
11091
- stateDir: "thoughts/brainstorms",
11092
- bindAddress: "127.0.0.1",
11093
- allowRemoteBind: false
11094
- },
11095
- mindmodel: {
11096
- overrideLogFile: "overrides.log",
11097
- reviewMaxRetries: 1,
11098
- reviewEnabled: true,
11099
- categoryGroups: ["stack", "architecture", "patterns", "style", "components", "domain", "ops"]
11100
- }
11101
- };
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";
11102
11957
 
11103
11958
  // src/utils/errors.ts
11104
11959
  function extractErrorMessage(e) {
@@ -11720,7 +12575,7 @@ function string(message$1) {
11720
12575
  }
11721
12576
  };
11722
12577
  }
11723
- function parse(schema, input, config$1) {
12578
+ function parse3(schema, input, config$1) {
11724
12579
  const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
11725
12580
  if (dataset.issues)
11726
12581
  throw new ValiError(dataset.issues);
@@ -11764,7 +12619,7 @@ var lexer = require_lexer();
11764
12619
  var lineCounter = require_line_counter();
11765
12620
  var parser = require_parser();
11766
12621
  var publicApi = require_public_api();
11767
- var visit = require_visit();
12622
+ var visit2 = require_visit();
11768
12623
  var $Composer = composer.Composer;
11769
12624
  var $Document = Document.Document;
11770
12625
  var $Schema = Schema.Schema;
@@ -11791,8 +12646,8 @@ var $parse = publicApi.parse;
11791
12646
  var $parseAllDocuments = publicApi.parseAllDocuments;
11792
12647
  var $parseDocument = publicApi.parseDocument;
11793
12648
  var $stringify = publicApi.stringify;
11794
- var $visit = visit.visit;
11795
- var $visitAsync = visit.visitAsync;
12649
+ var $visit = visit2.visit;
12650
+ var $visitAsync = visit2.visitAsync;
11796
12651
 
11797
12652
  // src/mindmodel/types.ts
11798
12653
  var CategorySchema = object({
@@ -11807,7 +12662,7 @@ var ManifestSchema = object({
11807
12662
  });
11808
12663
  function parseManifest(yamlContent) {
11809
12664
  const parsed = $parse(yamlContent);
11810
- return parse(ManifestSchema, parsed);
12665
+ return parse3(ManifestSchema, parsed);
11811
12666
  }
11812
12667
 
11813
12668
  // src/mindmodel/loader.ts
@@ -11827,7 +12682,7 @@ async function loadMindmodel(projectDir) {
11827
12682
  manifest
11828
12683
  };
11829
12684
  } catch (error) {
11830
- console.warn(`[micode] Failed to load mindmodel manifest: ${error}`);
12685
+ log.warn("mindmodel", `Failed to load manifest: ${error}`);
11831
12686
  return null;
11832
12687
  }
11833
12688
  }
@@ -11846,7 +12701,7 @@ async function loadExamples(mindmodel, categoryPaths) {
11846
12701
  content
11847
12702
  });
11848
12703
  } catch {
11849
- console.warn(`[micode] Failed to load mindmodel example: ${categoryPath}`);
12704
+ log.warn("mindmodel", `Failed to load example: ${categoryPath}`);
11850
12705
  }
11851
12706
  }
11852
12707
  return examples;
@@ -12205,6 +13060,165 @@ function createContextWindowMonitorHook(ctx, hookConfig) {
12205
13060
  };
12206
13061
  }
12207
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
+
12208
13222
  // src/hooks/file-ops-tracker.ts
12209
13223
  var sessionFileOps = new Map;
12210
13224
  function getOrCreateOps(sessionID) {
@@ -12495,7 +13509,7 @@ __export(exports_external, {
12495
13509
  pipe: () => pipe2,
12496
13510
  partialRecord: () => partialRecord,
12497
13511
  parseAsync: () => parseAsync2,
12498
- parse: () => parse4,
13512
+ parse: () => parse6,
12499
13513
  overwrite: () => _overwrite,
12500
13514
  optional: () => optional2,
12501
13515
  object: () => object2,
@@ -12685,7 +13699,7 @@ __export(exports_core2, {
12685
13699
  regexes: () => exports_regexes,
12686
13700
  prettifyError: () => prettifyError,
12687
13701
  parseAsync: () => parseAsync,
12688
- parse: () => parse2,
13702
+ parse: () => parse4,
12689
13703
  locales: () => exports_locales,
12690
13704
  isValidJWT: () => isValidJWT,
12691
13705
  isValidBase64URL: () => isValidBase64URL,
@@ -13784,7 +14798,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
13784
14798
  }
13785
14799
  return result.value;
13786
14800
  };
13787
- var parse2 = /* @__PURE__ */ _parse($ZodRealError);
14801
+ var parse4 = /* @__PURE__ */ _parse($ZodRealError);
13788
14802
  var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
13789
14803
  const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };
13790
14804
  let result = schema._zod.run({ value, issues: [] }, ctx);
@@ -16302,10 +17316,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
16302
17316
  throw new Error("implement() must be called with a function");
16303
17317
  }
16304
17318
  return function(...args) {
16305
- const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
17319
+ const parsedArgs = inst._def.input ? parse4(inst._def.input, args) : args;
16306
17320
  const result = Reflect.apply(func, this, parsedArgs);
16307
17321
  if (inst._def.output) {
16308
- return parse2(inst._def.output, result);
17322
+ return parse4(inst._def.output, result);
16309
17323
  }
16310
17324
  return result;
16311
17325
  };
@@ -22814,13 +23828,13 @@ function _stringbool(Classes, _params) {
22814
23828
  });
22815
23829
  return codec;
22816
23830
  }
22817
- function _stringFormat(Class2, format, fnOrRegex, _params = {}) {
23831
+ function _stringFormat(Class2, format2, fnOrRegex, _params = {}) {
22818
23832
  const params = normalizeParams(_params);
22819
23833
  const def = {
22820
23834
  ...normalizeParams(_params),
22821
23835
  check: "string_format",
22822
23836
  type: "string",
22823
- format,
23837
+ format: format2,
22824
23838
  fn: typeof fnOrRegex === "function" ? fnOrRegex : (val) => fnOrRegex.test(val),
22825
23839
  ...params
22826
23840
  };
@@ -22882,13 +23896,13 @@ class JSONSchemaGenerator {
22882
23896
  case "string": {
22883
23897
  const json = _json;
22884
23898
  json.type = "string";
22885
- const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
23899
+ const { minimum, maximum, format: format2, patterns, contentEncoding } = schema._zod.bag;
22886
23900
  if (typeof minimum === "number")
22887
23901
  json.minLength = minimum;
22888
23902
  if (typeof maximum === "number")
22889
23903
  json.maxLength = maximum;
22890
- if (format) {
22891
- json.format = formatMap[format] ?? format;
23904
+ if (format2) {
23905
+ json.format = formatMap[format2] ?? format2;
22892
23906
  if (json.format === "")
22893
23907
  delete json.format;
22894
23908
  }
@@ -22911,8 +23925,8 @@ class JSONSchemaGenerator {
22911
23925
  }
22912
23926
  case "number": {
22913
23927
  const json = _json;
22914
- const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
22915
- 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"))
22916
23930
  json.type = "integer";
22917
23931
  else
22918
23932
  json.type = "number";
@@ -23713,7 +24727,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
23713
24727
  });
23714
24728
 
23715
24729
  // node_modules/zod/v4/classic/parse.js
23716
- var parse4 = /* @__PURE__ */ _parse(ZodRealError);
24730
+ var parse6 = /* @__PURE__ */ _parse(ZodRealError);
23717
24731
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
23718
24732
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
23719
24733
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -23747,7 +24761,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
23747
24761
  reg.add(inst, meta);
23748
24762
  return inst;
23749
24763
  };
23750
- inst.parse = (data, params) => parse4(inst, data, params, { callee: inst.parse });
24764
+ inst.parse = (data, params) => parse6(inst, data, params, { callee: inst.parse });
23751
24765
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
23752
24766
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
23753
24767
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -24012,8 +25026,8 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
24012
25026
  $ZodCustomStringFormat.init(inst, def);
24013
25027
  ZodStringFormat.init(inst, def);
24014
25028
  });
24015
- function stringFormat(format, fnOrRegex, _params = {}) {
24016
- return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
25029
+ function stringFormat(format2, fnOrRegex, _params = {}) {
25030
+ return _stringFormat(ZodCustomStringFormat, format2, fnOrRegex, _params);
24017
25031
  }
24018
25032
  function hostname2(_params) {
24019
25033
  return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
@@ -24023,11 +25037,11 @@ function hex2(_params) {
24023
25037
  }
24024
25038
  function hash(alg, params) {
24025
25039
  const enc = params?.enc ?? "hex";
24026
- const format = `${alg}_${enc}`;
24027
- const regex = exports_regexes[format];
25040
+ const format2 = `${alg}_${enc}`;
25041
+ const regex = exports_regexes[format2];
24028
25042
  if (!regex)
24029
- throw new Error(`Unrecognized hash format: ${format}`);
24030
- return _stringFormat(ZodCustomStringFormat, format, regex, params);
25043
+ throw new Error(`Unrecognized hash format: ${format2}`);
25044
+ return _stringFormat(ZodCustomStringFormat, format2, regex, params);
24031
25045
  }
24032
25046
  var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
24033
25047
  $ZodNumber.init(inst, def);
@@ -24802,13 +25816,13 @@ Returns relevant code examples and patterns to follow.`,
24802
25816
  if (categories.length === 0) {
24803
25817
  return "No specific patterns found for this task. Proceed using general best practices.";
24804
25818
  }
24805
- log.info("mindmodel", `Matched categories: ${categories.join(", ")}`);
25819
+ log.debug("mindmodel", `Matched categories: ${categories.join(", ")}`);
24806
25820
  const examples = await loadExamples(mindmodel, categories);
24807
25821
  if (examples.length === 0) {
24808
25822
  return "Categories matched but no examples found. Proceed using general best practices.";
24809
25823
  }
24810
25824
  const formatted = formatExamplesForInjection(examples);
24811
- log.info("mindmodel", `Returning ${examples.length} examples`);
25825
+ log.debug("mindmodel", `Returning ${examples.length} examples`);
24812
25826
  return formatted;
24813
25827
  } catch (error45) {
24814
25828
  log.warn("mindmodel", `Lookup failed: ${error45 instanceof Error ? error45.message : "unknown"}`);
@@ -24830,7 +25844,7 @@ function hashTask(task) {
24830
25844
  return hash2.toString(36);
24831
25845
  }
24832
25846
 
24833
- class LRUCache {
25847
+ class LRUCache2 {
24834
25848
  maxSize;
24835
25849
  cache = new Map;
24836
25850
  constructor(maxSize) {
@@ -24862,7 +25876,7 @@ function createMindmodelInjectorHook(ctx) {
24862
25876
  let cachedMindmodel2;
24863
25877
  let cachedSystemMd;
24864
25878
  let pendingInjection = null;
24865
- const matchedTasks = new LRUCache(2000);
25879
+ const matchedTasks = new LRUCache2(2000);
24866
25880
  async function getMindmodel2() {
24867
25881
  if (cachedMindmodel2 === undefined) {
24868
25882
  cachedMindmodel2 = await loadMindmodel(ctx.directory);
@@ -27957,7 +28971,7 @@ function createSessionStore(options = {}) {
27957
28971
  return store;
27958
28972
  }
27959
28973
  // src/octto/state/persistence.ts
27960
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmSync } from "fs";
28974
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, rmSync } from "fs";
27961
28975
  import { join as join10 } from "path";
27962
28976
  function validateSessionId(sessionId) {
27963
28977
  if (!/^[a-zA-Z0-9_-]+$/.test(sessionId)) {
@@ -27970,7 +28984,7 @@ function createStatePersistence(baseDir = STATE_DIR) {
27970
28984
  return join10(baseDir, `${sessionId}.json`);
27971
28985
  }
27972
28986
  function ensureDir() {
27973
- if (!existsSync2(baseDir)) {
28987
+ if (!existsSync3(baseDir)) {
27974
28988
  mkdirSync2(baseDir, { recursive: true });
27975
28989
  }
27976
28990
  }
@@ -27983,7 +28997,7 @@ function createStatePersistence(baseDir = STATE_DIR) {
27983
28997
  },
27984
28998
  async load(sessionId) {
27985
28999
  const filePath = getFilePath(sessionId);
27986
- if (!existsSync2(filePath)) {
29000
+ if (!existsSync3(filePath)) {
27987
29001
  return null;
27988
29002
  }
27989
29003
  const content = await Bun.file(filePath).text();
@@ -27991,12 +29005,12 @@ function createStatePersistence(baseDir = STATE_DIR) {
27991
29005
  },
27992
29006
  async delete(sessionId) {
27993
29007
  const filePath = getFilePath(sessionId);
27994
- if (existsSync2(filePath)) {
29008
+ if (existsSync3(filePath)) {
27995
29009
  rmSync(filePath);
27996
29010
  }
27997
29011
  },
27998
29012
  async list() {
27999
- if (!existsSync2(baseDir)) {
29013
+ if (!existsSync3(baseDir)) {
28000
29014
  return [];
28001
29015
  }
28002
29016
  const files = readdirSync(baseDir);
@@ -29249,9 +30263,6 @@ function createOcttoTools(sessions, client, tracker) {
29249
30263
  };
29250
30264
  }
29251
30265
 
29252
- // src/tools/pty/manager.ts
29253
- import { spawn as spawn3 } from "bun-pty";
29254
-
29255
30266
  // src/tools/pty/buffer.ts
29256
30267
  var parsed = parseInt(process.env.PTY_MAX_BUFFER_LINES || "50000", 10);
29257
30268
  var DEFAULT_MAX_LINES = isNaN(parsed) ? 50000 : parsed;
@@ -29294,7 +30305,6 @@ class RingBuffer {
29294
30305
  this.lines = [];
29295
30306
  }
29296
30307
  }
29297
-
29298
30308
  // src/tools/pty/manager.ts
29299
30309
  function generateId2() {
29300
30310
  const hex3 = Array.from(crypto.getRandomValues(new Uint8Array(4))).map((b) => b.toString(16).padStart(2, "0")).join("");
@@ -29303,7 +30313,19 @@ function generateId2() {
29303
30313
 
29304
30314
  class PTYManager {
29305
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
+ }
29306
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
+ }
29307
30329
  const id = generateId2();
29308
30330
  const args = opts.args ?? [];
29309
30331
  const workdir = opts.workdir ?? process.cwd();
@@ -29311,7 +30333,7 @@ class PTYManager {
29311
30333
  const title = opts.title ?? (`${opts.command} ${args.join(" ")}`.trim() || `Terminal ${id.slice(-4)}`);
29312
30334
  let ptyProcess;
29313
30335
  try {
29314
- ptyProcess = spawn3(opts.command, args, {
30336
+ ptyProcess = this.spawnFn(opts.command, args, {
29315
30337
  name: "xterm-256color",
29316
30338
  cols: 120,
29317
30339
  rows: 40,
@@ -29433,150 +30455,166 @@ class PTYManager {
29433
30455
  };
29434
30456
  }
29435
30457
  }
29436
- // src/tools/pty/tools/spawn.ts
29437
- var DESCRIPTION = `Spawns a new interactive PTY (pseudo-terminal) session that runs in the background.
29438
-
29439
- Unlike the built-in bash tool which runs commands synchronously and waits for completion, PTY sessions persist and allow you to:
29440
- - Run long-running processes (dev servers, watch modes, etc.)
29441
- - Send interactive input (including Ctrl+C, arrow keys, etc.)
29442
- - Read output at any time
29443
- - Manage multiple concurrent terminal sessions
29444
-
29445
- Usage:
29446
- - The \`command\` parameter is required (e.g., "npm", "python", "bash")
29447
- - Use \`args\` to pass arguments to the command (e.g., ["run", "dev"])
29448
- - Use \`workdir\` to set the working directory (defaults to project root)
29449
- - Use \`env\` to set additional environment variables
29450
- - Use \`title\` to give the session a human-readable name
29451
- - Use \`description\` for a clear, concise 5-10 word description (optional)
29452
-
29453
- Returns the session info including:
29454
- - \`id\`: Unique identifier (pty_XXXXXXXX) for use with other pty_* tools
29455
- - \`pid\`: Process ID
29456
- - \`status\`: Current status ("running")
29457
-
29458
- After spawning, use:
29459
- - \`pty_write\` to send input to the PTY
29460
- - \`pty_read\` to read output from the PTY
29461
- - \`pty_list\` to see all active PTY sessions
29462
- - \`pty_kill\` to terminate the PTY
29463
-
29464
- Examples:
29465
- - Start a dev server: command="npm", args=["run", "dev"], title="Dev Server"
29466
- - Start a Python REPL: command="python3", title="Python REPL"
29467
- - Run tests in watch mode: command="npm", args=["test", "--", "--watch"]`;
29468
- function createPtySpawnTool(manager) {
29469
- return tool({
29470
- description: DESCRIPTION,
29471
- args: {
29472
- command: tool.schema.string().describe("The command/executable to run"),
29473
- args: tool.schema.array(tool.schema.string()).optional().describe("Arguments to pass to the command"),
29474
- workdir: tool.schema.string().optional().describe("Working directory for the PTY session"),
29475
- env: tool.schema.record(tool.schema.string(), tool.schema.string()).optional().describe("Additional environment variables"),
29476
- title: tool.schema.string().optional().describe("Human-readable title for the session"),
29477
- description: tool.schema.string().optional().describe("Clear, concise description of what this PTY session is for in 5-10 words")
29478
- },
29479
- execute: async (args, ctx) => {
29480
- const info = manager.spawn({
29481
- command: args.command,
29482
- args: args.args,
29483
- workdir: args.workdir,
29484
- env: args.env,
29485
- title: args.title,
29486
- parentSessionId: ctx.sessionID
29487
- });
29488
- const output = [
29489
- `<pty_spawned>`,
29490
- `ID: ${info.id}`,
29491
- `Title: ${info.title}`,
29492
- `Command: ${info.command} ${info.args.join(" ")}`,
29493
- `Workdir: ${info.workdir}`,
29494
- `PID: ${info.pid}`,
29495
- `Status: ${info.status}`,
29496
- `</pty_spawned>`
29497
- ].join(`
29498
- `);
29499
- 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"));
29500
30480
  }
29501
- });
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
+ }
29502
30492
  }
29503
- // src/tools/pty/tools/write.ts
29504
- 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.
29505
30514
 
29506
30515
  Use this tool to:
29507
- - Type commands or text into an interactive terminal
29508
- - Send special key sequences (Ctrl+C, Enter, arrow keys, etc.)
29509
- - 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
29510
30519
 
29511
30520
  Usage:
29512
30521
  - \`id\`: The PTY session ID (from pty_spawn or pty_list)
29513
- - \`data\`: The input to send (text, commands, or escape sequences)
30522
+ - \`cleanup\`: If true, removes the session and frees the buffer (default: false)
29514
30523
 
29515
- Common escape sequences:
29516
- - Enter/newline: "\\n" or "\\r"
29517
- - Ctrl+C (interrupt): "\\x03"
29518
- - Ctrl+D (EOF): "\\x04"
29519
- - Ctrl+Z (suspend): "\\x1a"
29520
- - Tab: "\\t"
29521
- - Arrow Up: "\\x1b[A"
29522
- - Arrow Down: "\\x1b[B"
29523
- - Arrow Right: "\\x1b[C"
29524
- - 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
29525
30529
 
29526
- 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"
29527
30534
 
29528
30535
  Examples:
29529
- - Send a command: data="ls -la\\n"
29530
- - Interrupt a process: data="\\x03"
29531
- - Answer a prompt: data="yes\\n"`;
29532
- function parseEscapeSequences(input) {
29533
- return input.replace(/\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[nrt0\\])/g, (match, seq) => {
29534
- if (seq.startsWith("x")) {
29535
- return String.fromCharCode(parseInt(seq.slice(1), 16));
29536
- }
29537
- if (seq.startsWith("u")) {
29538
- return String.fromCharCode(parseInt(seq.slice(1), 16));
29539
- }
29540
- switch (seq) {
29541
- case "n":
29542
- return `
29543
- `;
29544
- case "r":
29545
- return "\r";
29546
- case "t":
29547
- return "\t";
29548
- case "0":
29549
- return "\x00";
29550
- case "\\":
29551
- return "\\";
29552
- default:
29553
- 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
+ `);
29554
30567
  }
29555
30568
  });
29556
30569
  }
29557
- 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) {
29558
30594
  return tool({
29559
30595
  description: DESCRIPTION2,
29560
- args: {
29561
- id: tool.schema.string().describe("The PTY session ID (e.g., pty_a1b2c3d4)"),
29562
- data: tool.schema.string().describe("The input data to send to the PTY")
29563
- },
29564
- execute: async (args) => {
29565
- const session = manager.get(args.id);
29566
- if (!session) {
29567
- throw new Error(`PTY session '${args.id}' not found. Use pty_list to see active sessions.`);
29568
- }
29569
- if (session.status !== "running") {
29570
- 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>`;
29571
30603
  }
29572
- const parsedData = parseEscapeSequences(args.data);
29573
- const success2 = manager.write(args.id, parsedData);
29574
- if (!success2) {
29575
- 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("");
29576
30613
  }
29577
- const preview = args.data.length > 50 ? `${args.data.slice(0, 50)}...` : args.data;
29578
- const displayPreview = preview.replace(/\x03/g, "^C").replace(/\x04/g, "^D").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
29579
- 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
+ `);
29580
30618
  }
29581
30619
  });
29582
30620
  }
@@ -29710,112 +30748,150 @@ function createPtyReadTool(manager) {
29710
30748
  }
29711
30749
  });
29712
30750
  }
29713
- // src/tools/pty/tools/list.ts
29714
- 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.
29715
30753
 
29716
- Use this tool to:
29717
- - See all running and exited PTY sessions
29718
- - Get session IDs for use with other pty_* tools
29719
- - Check the status and output line count of each session
29720
- - 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
29721
30759
 
29722
- Returns for each session:
29723
- - \`id\`: Unique identifier for use with other tools
29724
- - \`title\`: Human-readable name
29725
- - \`command\`: The command that was executed
29726
- - \`status\`: Current status (running, exited, killed)
29727
- - \`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
29728
30770
  - \`pid\`: Process ID
29729
- - \`lineCount\`: Number of lines in the output buffer
29730
- - \`createdAt\`: When the session was created
30771
+ - \`status\`: Current status ("running")
29731
30772
 
29732
- Tips:
29733
- - Use the session ID with pty_read, pty_write, or pty_kill
29734
- - Sessions remain in the list after exit until explicitly cleaned up with pty_kill
29735
- - This allows you to compare output from multiple sessions`;
29736
- 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) {
29737
30784
  return tool({
29738
30785
  description: DESCRIPTION4,
29739
- args: {},
29740
- execute: async () => {
29741
- const sessions = manager.list();
29742
- if (sessions.length === 0) {
29743
- return `<pty_list>
29744
- No active PTY sessions.
29745
- </pty_list>`;
29746
- }
29747
- const lines = ["<pty_list>"];
29748
- for (const session of sessions) {
29749
- const exitInfo = session.exitCode !== undefined ? ` (exit: ${session.exitCode})` : "";
29750
- lines.push(`[${session.id}] ${session.title}`);
29751
- lines.push(` Command: ${session.command} ${session.args.join(" ")}`);
29752
- lines.push(` Status: ${session.status}${exitInfo}`);
29753
- lines.push(` PID: ${session.pid} | Lines: ${session.lineCount} | Workdir: ${session.workdir}`);
29754
- lines.push(` Created: ${session.createdAt.toISOString()}`);
29755
- lines.push("");
29756
- }
29757
- lines.push(`Total: ${sessions.length} session(s)`);
29758
- lines.push("</pty_list>");
29759
- 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(`
29760
30813
  `);
30814
+ return output;
29761
30815
  }
29762
30816
  });
29763
30817
  }
29764
- // src/tools/pty/tools/kill.ts
29765
- 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.
29766
30820
 
29767
30821
  Use this tool to:
29768
- - Stop a running process (sends SIGTERM)
29769
- - Clean up an exited session to free memory
29770
- - 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
29771
30825
 
29772
30826
  Usage:
29773
30827
  - \`id\`: The PTY session ID (from pty_spawn or pty_list)
29774
- - \`cleanup\`: If true, removes the session and frees the buffer (default: false)
30828
+ - \`data\`: The input to send (text, commands, or escape sequences)
29775
30829
 
29776
- Behavior:
29777
- - If the session is running, it will be killed (status becomes "killed")
29778
- - If cleanup=false (default), the session remains in the list with its output buffer intact
29779
- - If cleanup=true, the session is removed entirely and the buffer is freed
29780
- - 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"
29781
30840
 
29782
- Tips:
29783
- - Use cleanup=false if you might want to read the output later
29784
- - Use cleanup=true when you're done with the session entirely
29785
- - To send Ctrl+C instead of killing, use pty_write with data="\\x03"
30841
+ Returns success or error message.
29786
30842
 
29787
30843
  Examples:
29788
- - Kill but keep logs: cleanup=false (or omit)
29789
- - Kill and remove: cleanup=true`;
29790
- 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) {
29791
30873
  return tool({
29792
30874
  description: DESCRIPTION5,
29793
30875
  args: {
29794
30876
  id: tool.schema.string().describe("The PTY session ID (e.g., pty_a1b2c3d4)"),
29795
- 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")
29796
30878
  },
29797
30879
  execute: async (args) => {
29798
30880
  const session = manager.get(args.id);
29799
30881
  if (!session) {
29800
30882
  throw new Error(`PTY session '${args.id}' not found. Use pty_list to see active sessions.`);
29801
30883
  }
29802
- const wasRunning = session.status === "running";
29803
- const cleanup = args.cleanup ?? false;
29804
- 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);
29805
30889
  if (!success2) {
29806
- throw new Error(`Failed to kill PTY session '${args.id}'.`);
30890
+ throw new Error(`Failed to write to PTY '${args.id}'.`);
29807
30891
  }
29808
- const action = wasRunning ? "Killed" : "Cleaned up";
29809
- const cleanupNote = cleanup ? " (session removed)" : " (session retained for log access)";
29810
- return [
29811
- `<pty_killed>`,
29812
- `${action}: ${args.id}${cleanupNote}`,
29813
- `Title: ${session.title}`,
29814
- `Command: ${session.command} ${session.args.join(" ")}`,
29815
- `Final line count: ${session.lineCount}`,
29816
- `</pty_killed>`
29817
- ].join(`
29818
- `);
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}"`;
29819
30895
  }
29820
30896
  });
29821
30897
  }
@@ -30004,6 +31080,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
30004
31080
  const commentCheckerHook = createCommentCheckerHook(ctx);
30005
31081
  const artifactAutoIndexHook = createArtifactAutoIndexHook(ctx);
30006
31082
  const fileOpsTrackerHook = createFileOpsTrackerHook(ctx);
31083
+ const fetchTrackerHook = createFetchTrackerHook(ctx);
30007
31084
  const fragmentInjectorHook = createFragmentInjectorHook(ctx, userConfig);
30008
31085
  if (userConfig?.fragments) {
30009
31086
  const knownAgentNames = new Set(Object.keys(agents));
@@ -30057,7 +31134,11 @@ var OpenCodeConfigPlugin = async (ctx) => {
30057
31134
  }
30058
31135
  });
30059
31136
  const ptyManager = new PTYManager;
30060
- 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) : {};
30061
31142
  const spawn_agent = createSpawnAgentTool(ctx);
30062
31143
  const batch_read = createBatchReadTool(ctx);
30063
31144
  const octtoSessionStore = createSessionStore();
@@ -30098,7 +31179,6 @@ var OpenCodeConfigPlugin = async (ctx) => {
30098
31179
  edit: "allow",
30099
31180
  bash: "allow",
30100
31181
  webfetch: "allow",
30101
- doom_loop: "allow",
30102
31182
  external_directory: "allow"
30103
31183
  };
30104
31184
  const mergedAgents = mergeAgentConfigs(agents, userConfig);
@@ -30154,7 +31234,7 @@ var OpenCodeConfigPlugin = async (ctx) => {
30154
31234
  ...output.options,
30155
31235
  thinking: {
30156
31236
  type: "enabled",
30157
- budget_tokens: 32000
31237
+ budgetTokens: 128000
30158
31238
  }
30159
31239
  };
30160
31240
  }
@@ -30215,6 +31295,7 @@ IMPORTANT:
30215
31295
  await contextInjectorHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
30216
31296
  await artifactAutoIndexHook["tool.execute.after"]({ tool: input.tool, args: input.args }, output);
30217
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);
30218
31299
  await constraintReviewerHook["tool.execute.after"]({ tool: input.tool, sessionID: input.sessionID, args: input.args }, output);
30219
31300
  },
30220
31301
  "experimental.chat.messages.transform": async (input, output) => {
@@ -30248,6 +31329,7 @@ IMPORTANT:
30248
31329
  thinkModeState.delete(sessionId);
30249
31330
  ptyManager.cleanupBySession(sessionId);
30250
31331
  constraintReviewerHook.cleanupSession(sessionId);
31332
+ fetchTrackerHook.cleanupSession(sessionId);
30251
31333
  const octtoSessions = octtoSessionsMap.get(sessionId);
30252
31334
  if (octtoSessions) {
30253
31335
  for (const octtoSessionId of octtoSessions) {
@@ -30262,6 +31344,7 @@ IMPORTANT:
30262
31344
  await tokenAwareTruncationHook.event({ event });
30263
31345
  await contextWindowMonitorHook.event({ event });
30264
31346
  await fileOpsTrackerHook.event({ event });
31347
+ await fetchTrackerHook.event({ event });
30265
31348
  }
30266
31349
  };
30267
31350
  };