oh-my-opencode 3.0.1 → 3.1.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.
Files changed (37) hide show
  1. package/dist/agents/utils.d.ts +3 -2
  2. package/dist/cli/index.js +304 -105
  3. package/dist/config/index.d.ts +2 -2
  4. package/dist/config/schema.d.ts +59 -0
  5. package/dist/features/background-agent/manager.d.ts +13 -2
  6. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  7. package/dist/features/builtin-skills/index.d.ts +1 -1
  8. package/dist/features/builtin-skills/skills.d.ts +5 -1
  9. package/dist/features/builtin-skills/skills.test.d.ts +1 -0
  10. package/dist/features/opencode-skill-loader/skill-content.d.ts +3 -2
  11. package/dist/features/tmux-subagent/action-executor.d.ts +22 -0
  12. package/dist/features/tmux-subagent/decision-engine.d.ts +38 -0
  13. package/dist/features/tmux-subagent/decision-engine.test.d.ts +1 -0
  14. package/dist/features/tmux-subagent/index.d.ts +5 -0
  15. package/dist/features/tmux-subagent/manager.d.ts +54 -0
  16. package/dist/features/tmux-subagent/manager.test.d.ts +1 -0
  17. package/dist/features/tmux-subagent/pane-state-querier.d.ts +2 -0
  18. package/dist/features/tmux-subagent/types.d.ts +51 -0
  19. package/dist/hooks/category-skill-reminder/index.d.ts +22 -0
  20. package/dist/hooks/category-skill-reminder/index.test.d.ts +1 -0
  21. package/dist/hooks/index.d.ts +2 -0
  22. package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +2 -0
  23. package/dist/hooks/sisyphus-junior-notepad/index.d.ts +12 -0
  24. package/dist/index.js +2798 -918
  25. package/dist/shared/connected-providers-cache.d.ts +52 -0
  26. package/dist/shared/data-path.d.ts +16 -0
  27. package/dist/shared/index.d.ts +3 -0
  28. package/dist/shared/model-availability.d.ts +4 -5
  29. package/dist/shared/session-utils.d.ts +2 -0
  30. package/dist/shared/tmux/constants.d.ts +5 -0
  31. package/dist/shared/tmux/index.d.ts +3 -0
  32. package/dist/shared/tmux/tmux-utils.d.ts +17 -0
  33. package/dist/shared/tmux/tmux-utils.test.d.ts +1 -0
  34. package/dist/shared/tmux/types.d.ts +4 -0
  35. package/dist/tools/delegate-task/tools.d.ts +8 -1
  36. package/dist/tools/slashcommand/tools.test.d.ts +1 -0
  37. package/package.json +8 -8
package/dist/cli/index.js CHANGED
@@ -4919,6 +4919,18 @@ import * as os2 from "os";
4919
4919
  function getDataDir() {
4920
4920
  return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
4921
4921
  }
4922
+ function getOpenCodeStorageDir() {
4923
+ return path2.join(getDataDir(), "opencode", "storage");
4924
+ }
4925
+ function getCacheDir() {
4926
+ return process.env.XDG_CACHE_HOME ?? path2.join(os2.homedir(), ".cache");
4927
+ }
4928
+ function getOmoOpenCodeCacheDir() {
4929
+ return path2.join(getCacheDir(), "oh-my-opencode");
4930
+ }
4931
+ function getOpenCodeCacheDir() {
4932
+ return path2.join(getCacheDir(), "opencode");
4933
+ }
4922
4934
  var init_data_path = () => {};
4923
4935
 
4924
4936
  // src/shared/config-errors.ts
@@ -5980,6 +5992,7 @@ var init_model_requirements = __esm(() => {
5980
5992
  explore: {
5981
5993
  fallbackChain: [
5982
5994
  { providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
5995
+ { providers: ["github-copilot"], model: "gpt-5-mini" },
5983
5996
  { providers: ["opencode"], model: "gpt-5-nano" }
5984
5997
  ]
5985
5998
  },
@@ -6075,28 +6088,165 @@ var init_model_requirements = __esm(() => {
6075
6088
  };
6076
6089
  });
6077
6090
 
6078
- // src/shared/model-availability.ts
6079
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
6080
- import { homedir as homedir3 } from "os";
6091
+ // src/shared/connected-providers-cache.ts
6092
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
6081
6093
  import { join as join4 } from "path";
6082
- function getOpenCodeCacheDir() {
6083
- const xdgCache = process.env.XDG_CACHE_HOME;
6084
- if (xdgCache)
6085
- return join4(xdgCache, "opencode");
6086
- return join4(homedir3(), ".cache", "opencode");
6094
+ function getCacheFilePath(filename) {
6095
+ return join4(getOmoOpenCodeCacheDir(), filename);
6087
6096
  }
6088
- function isModelCacheAvailable() {
6089
- const cacheFile = join4(getOpenCodeCacheDir(), "models.json");
6097
+ function ensureCacheDir() {
6098
+ const cacheDir = getOmoOpenCodeCacheDir();
6099
+ if (!existsSync3(cacheDir)) {
6100
+ mkdirSync(cacheDir, { recursive: true });
6101
+ }
6102
+ }
6103
+ function hasConnectedProvidersCache() {
6104
+ const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
6090
6105
  return existsSync3(cacheFile);
6091
6106
  }
6107
+ function writeConnectedProvidersCache(connected) {
6108
+ ensureCacheDir();
6109
+ const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
6110
+ const data = {
6111
+ connected,
6112
+ updatedAt: new Date().toISOString()
6113
+ };
6114
+ try {
6115
+ writeFileSync(cacheFile, JSON.stringify(data, null, 2));
6116
+ log("[connected-providers-cache] Cache written", { count: connected.length });
6117
+ } catch (err) {
6118
+ log("[connected-providers-cache] Error writing cache", { error: String(err) });
6119
+ }
6120
+ }
6121
+ function hasProviderModelsCache() {
6122
+ const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE);
6123
+ return existsSync3(cacheFile);
6124
+ }
6125
+ function writeProviderModelsCache(data) {
6126
+ ensureCacheDir();
6127
+ const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE);
6128
+ const cacheData = {
6129
+ ...data,
6130
+ updatedAt: new Date().toISOString()
6131
+ };
6132
+ try {
6133
+ writeFileSync(cacheFile, JSON.stringify(cacheData, null, 2));
6134
+ log("[connected-providers-cache] Provider-models cache written", {
6135
+ providerCount: Object.keys(data.models).length
6136
+ });
6137
+ } catch (err) {
6138
+ log("[connected-providers-cache] Error writing provider-models cache", { error: String(err) });
6139
+ }
6140
+ }
6141
+ async function updateConnectedProvidersCache(client) {
6142
+ if (!client?.provider?.list) {
6143
+ log("[connected-providers-cache] client.provider.list not available");
6144
+ return;
6145
+ }
6146
+ try {
6147
+ const result = await client.provider.list();
6148
+ const connected = result.data?.connected ?? [];
6149
+ log("[connected-providers-cache] Fetched connected providers", { count: connected.length, providers: connected });
6150
+ writeConnectedProvidersCache(connected);
6151
+ if (client.model?.list) {
6152
+ try {
6153
+ const modelsResult = await client.model.list();
6154
+ const models = modelsResult.data ?? [];
6155
+ const modelsByProvider = {};
6156
+ for (const model of models) {
6157
+ if (!modelsByProvider[model.provider]) {
6158
+ modelsByProvider[model.provider] = [];
6159
+ }
6160
+ modelsByProvider[model.provider].push(model.id);
6161
+ }
6162
+ writeProviderModelsCache({
6163
+ models: modelsByProvider,
6164
+ connected
6165
+ });
6166
+ log("[connected-providers-cache] Provider-models cache updated", {
6167
+ providerCount: Object.keys(modelsByProvider).length,
6168
+ totalModels: models.length
6169
+ });
6170
+ } catch (modelErr) {
6171
+ log("[connected-providers-cache] Error fetching models", { error: String(modelErr) });
6172
+ }
6173
+ }
6174
+ } catch (err) {
6175
+ log("[connected-providers-cache] Error updating cache", { error: String(err) });
6176
+ }
6177
+ }
6178
+ var CONNECTED_PROVIDERS_CACHE_FILE = "connected-providers.json", PROVIDER_MODELS_CACHE_FILE = "provider-models.json";
6179
+ var init_connected_providers_cache = __esm(() => {
6180
+ init_logger();
6181
+ init_data_path();
6182
+ });
6183
+
6184
+ // src/shared/model-availability.ts
6185
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
6186
+ import { join as join5 } from "path";
6187
+ function isModelCacheAvailable() {
6188
+ if (hasProviderModelsCache()) {
6189
+ return true;
6190
+ }
6191
+ const cacheFile = join5(getOpenCodeCacheDir(), "models.json");
6192
+ return existsSync4(cacheFile);
6193
+ }
6092
6194
  var init_model_availability = __esm(() => {
6093
6195
  init_logger();
6196
+ init_data_path();
6197
+ init_connected_providers_cache();
6094
6198
  });
6095
6199
 
6096
6200
  // src/shared/model-resolver.ts
6097
6201
  var init_model_resolver = __esm(() => {
6098
6202
  init_logger();
6099
6203
  init_model_availability();
6204
+ init_connected_providers_cache();
6205
+ });
6206
+
6207
+ // src/features/hook-message-injector/constants.ts
6208
+ import { join as join6 } from "path";
6209
+ var OPENCODE_STORAGE, MESSAGE_STORAGE, PART_STORAGE;
6210
+ var init_constants = __esm(() => {
6211
+ init_data_path();
6212
+ OPENCODE_STORAGE = getOpenCodeStorageDir();
6213
+ MESSAGE_STORAGE = join6(OPENCODE_STORAGE, "message");
6214
+ PART_STORAGE = join6(OPENCODE_STORAGE, "part");
6215
+ });
6216
+
6217
+ // src/features/hook-message-injector/injector.ts
6218
+ var init_injector = __esm(() => {
6219
+ init_constants();
6220
+ });
6221
+
6222
+ // src/features/hook-message-injector/index.ts
6223
+ var init_hook_message_injector = __esm(() => {
6224
+ init_injector();
6225
+ init_constants();
6226
+ });
6227
+
6228
+ // src/shared/session-utils.ts
6229
+ var init_session_utils = __esm(() => {
6230
+ init_hook_message_injector();
6231
+ });
6232
+ // src/shared/tmux/constants.ts
6233
+ var SESSION_TIMEOUT_MS;
6234
+ var init_constants2 = __esm(() => {
6235
+ SESSION_TIMEOUT_MS = 10 * 60 * 1000;
6236
+ });
6237
+
6238
+ // src/tools/interactive-bash/utils.ts
6239
+ var init_utils = () => {};
6240
+
6241
+ // src/shared/tmux/tmux-utils.ts
6242
+ var init_tmux_utils = __esm(() => {
6243
+ init_utils();
6244
+ });
6245
+
6246
+ // src/shared/tmux/index.ts
6247
+ var init_tmux = __esm(() => {
6248
+ init_constants2();
6249
+ init_tmux_utils();
6100
6250
  });
6101
6251
 
6102
6252
  // src/shared/index.ts
@@ -6126,6 +6276,9 @@ var init_shared = __esm(() => {
6126
6276
  init_model_requirements();
6127
6277
  init_model_resolver();
6128
6278
  init_model_availability();
6279
+ init_connected_providers_cache();
6280
+ init_session_utils();
6281
+ init_tmux();
6129
6282
  });
6130
6283
 
6131
6284
  // src/cli/model-fallback.ts
@@ -6205,6 +6358,8 @@ function generateModelConfig(config) {
6205
6358
  agents[role] = { model: "anthropic/claude-haiku-4-5" };
6206
6359
  } else if (avail.opencodeZen) {
6207
6360
  agents[role] = { model: "opencode/claude-haiku-4-5" };
6361
+ } else if (avail.copilot) {
6362
+ agents[role] = { model: "github-copilot/gpt-5-mini" };
6208
6363
  } else {
6209
6364
  agents[role] = { model: "opencode/gpt-5-nano" };
6210
6365
  }
@@ -6241,7 +6396,7 @@ var init_model_fallback = __esm(() => {
6241
6396
  });
6242
6397
 
6243
6398
  // src/cli/config-manager.ts
6244
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync, statSync } from "fs";
6399
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, statSync } from "fs";
6245
6400
  function initConfigContext(binary2, version) {
6246
6401
  const paths = getOpenCodeConfigPaths({ binary: binary2, version });
6247
6402
  configContext = { binary: binary2, version, paths };
@@ -6331,10 +6486,10 @@ async function getPluginNameWithVersion(currentVersion) {
6331
6486
  function detectConfigFormat() {
6332
6487
  const configJsonc = getConfigJsonc();
6333
6488
  const configJson = getConfigJson();
6334
- if (existsSync4(configJsonc)) {
6489
+ if (existsSync5(configJsonc)) {
6335
6490
  return { format: "jsonc", path: configJsonc };
6336
6491
  }
6337
- if (existsSync4(configJson)) {
6492
+ if (existsSync5(configJson)) {
6338
6493
  return { format: "json", path: configJson };
6339
6494
  }
6340
6495
  return { format: "none", path: configJson };
@@ -6348,7 +6503,7 @@ function parseConfigWithError(path3) {
6348
6503
  if (stat.size === 0) {
6349
6504
  return { config: null, error: `Config file is empty: ${path3}. Delete it or add valid JSON content.` };
6350
6505
  }
6351
- const content = readFileSync3(path3, "utf-8");
6506
+ const content = readFileSync4(path3, "utf-8");
6352
6507
  if (isEmptyOrWhitespace(content)) {
6353
6508
  return { config: null, error: `Config file contains only whitespace: ${path3}. Delete it or add valid JSON content.` };
6354
6509
  }
@@ -6366,8 +6521,8 @@ function parseConfigWithError(path3) {
6366
6521
  }
6367
6522
  function ensureConfigDir() {
6368
6523
  const configDir = getConfigDir();
6369
- if (!existsSync4(configDir)) {
6370
- mkdirSync(configDir, { recursive: true });
6524
+ if (!existsSync5(configDir)) {
6525
+ mkdirSync2(configDir, { recursive: true });
6371
6526
  }
6372
6527
  }
6373
6528
  async function addPluginToOpenCodeConfig(currentVersion) {
@@ -6381,7 +6536,7 @@ async function addPluginToOpenCodeConfig(currentVersion) {
6381
6536
  try {
6382
6537
  if (format2 === "none") {
6383
6538
  const config2 = { plugin: [pluginEntry] };
6384
- writeFileSync(path3, JSON.stringify(config2, null, 2) + `
6539
+ writeFileSync2(path3, JSON.stringify(config2, null, 2) + `
6385
6540
  `);
6386
6541
  return { success: true, configPath: path3 };
6387
6542
  }
@@ -6402,7 +6557,7 @@ async function addPluginToOpenCodeConfig(currentVersion) {
6402
6557
  }
6403
6558
  config.plugin = plugins;
6404
6559
  if (format2 === "jsonc") {
6405
- const content = readFileSync3(path3, "utf-8");
6560
+ const content = readFileSync4(path3, "utf-8");
6406
6561
  const pluginArrayRegex = /"plugin"\s*:\s*\[([\s\S]*?)\]/;
6407
6562
  const match = content.match(pluginArrayRegex);
6408
6563
  if (match) {
@@ -6411,14 +6566,14 @@ async function addPluginToOpenCodeConfig(currentVersion) {
6411
6566
  const newContent = content.replace(pluginArrayRegex, `"plugin": [
6412
6567
  ${formattedPlugins}
6413
6568
  ]`);
6414
- writeFileSync(path3, newContent);
6569
+ writeFileSync2(path3, newContent);
6415
6570
  } else {
6416
6571
  const newContent = content.replace(/^(\s*\{)/, `$1
6417
6572
  "plugin": ["${pluginEntry}"],`);
6418
- writeFileSync(path3, newContent);
6573
+ writeFileSync2(path3, newContent);
6419
6574
  }
6420
6575
  } else {
6421
- writeFileSync(path3, JSON.stringify(config, null, 2) + `
6576
+ writeFileSync2(path3, JSON.stringify(config, null, 2) + `
6422
6577
  `);
6423
6578
  }
6424
6579
  return { success: true, configPath: path3 };
@@ -6451,34 +6606,34 @@ function writeOmoConfig(installConfig) {
6451
6606
  const omoConfigPath = getOmoConfig();
6452
6607
  try {
6453
6608
  const newConfig = generateOmoConfig(installConfig);
6454
- if (existsSync4(omoConfigPath)) {
6609
+ if (existsSync5(omoConfigPath)) {
6455
6610
  try {
6456
6611
  const stat = statSync(omoConfigPath);
6457
- const content = readFileSync3(omoConfigPath, "utf-8");
6612
+ const content = readFileSync4(omoConfigPath, "utf-8");
6458
6613
  if (stat.size === 0 || isEmptyOrWhitespace(content)) {
6459
- writeFileSync(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6614
+ writeFileSync2(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6460
6615
  `);
6461
6616
  return { success: true, configPath: omoConfigPath };
6462
6617
  }
6463
6618
  const existing = parseJsonc(content);
6464
6619
  if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
6465
- writeFileSync(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6620
+ writeFileSync2(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6466
6621
  `);
6467
6622
  return { success: true, configPath: omoConfigPath };
6468
6623
  }
6469
6624
  const merged = deepMerge(existing, newConfig);
6470
- writeFileSync(omoConfigPath, JSON.stringify(merged, null, 2) + `
6625
+ writeFileSync2(omoConfigPath, JSON.stringify(merged, null, 2) + `
6471
6626
  `);
6472
6627
  } catch (parseErr) {
6473
6628
  if (parseErr instanceof SyntaxError) {
6474
- writeFileSync(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6629
+ writeFileSync2(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6475
6630
  `);
6476
6631
  return { success: true, configPath: omoConfigPath };
6477
6632
  }
6478
6633
  throw parseErr;
6479
6634
  }
6480
6635
  } else {
6481
- writeFileSync(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6636
+ writeFileSync2(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
6482
6637
  `);
6483
6638
  }
6484
6639
  return { success: true, configPath: omoConfigPath };
@@ -6540,7 +6695,7 @@ async function addAuthPlugins(config) {
6540
6695
  }
6541
6696
  }
6542
6697
  const newConfig = { ...existingConfig ?? {}, plugin: plugins };
6543
- writeFileSync(path3, JSON.stringify(newConfig, null, 2) + `
6698
+ writeFileSync2(path3, JSON.stringify(newConfig, null, 2) + `
6544
6699
  `);
6545
6700
  return { success: true, configPath: path3 };
6546
6701
  } catch (err) {
@@ -6612,7 +6767,7 @@ function addProviderConfig(config) {
6612
6767
  if (Object.keys(providers).length > 0) {
6613
6768
  newConfig.provider = providers;
6614
6769
  }
6615
- writeFileSync(path3, JSON.stringify(newConfig, null, 2) + `
6770
+ writeFileSync2(path3, JSON.stringify(newConfig, null, 2) + `
6616
6771
  `);
6617
6772
  return { success: true, configPath: path3 };
6618
6773
  } catch (err) {
@@ -6621,11 +6776,11 @@ function addProviderConfig(config) {
6621
6776
  }
6622
6777
  function detectProvidersFromOmoConfig() {
6623
6778
  const omoConfigPath = getOmoConfig();
6624
- if (!existsSync4(omoConfigPath)) {
6779
+ if (!existsSync5(omoConfigPath)) {
6625
6780
  return { hasOpenAI: true, hasOpencodeZen: true, hasZaiCodingPlan: false };
6626
6781
  }
6627
6782
  try {
6628
- const content = readFileSync3(omoConfigPath, "utf-8");
6783
+ const content = readFileSync4(omoConfigPath, "utf-8");
6629
6784
  const omoConfig = parseJsonc(content);
6630
6785
  if (!omoConfig || typeof omoConfig !== "object") {
6631
6786
  return { hasOpenAI: true, hasOpencodeZen: true, hasZaiCodingPlan: false };
@@ -6733,7 +6888,7 @@ var init_config_manager = __esm(() => {
6733
6888
  // src/hooks/auto-update-checker/constants.ts
6734
6889
  import * as path3 from "path";
6735
6890
  import * as os3 from "os";
6736
- function getCacheDir() {
6891
+ function getCacheDir2() {
6737
6892
  if (process.platform === "win32") {
6738
6893
  return path3.join(process.env.LOCALAPPDATA ?? os3.homedir(), "opencode");
6739
6894
  }
@@ -6745,10 +6900,10 @@ function getWindowsAppdataDir() {
6745
6900
  return process.env.APPDATA ?? path3.join(os3.homedir(), "AppData", "Roaming");
6746
6901
  }
6747
6902
  var PACKAGE_NAME2 = "oh-my-opencode", NPM_REGISTRY_URL, NPM_FETCH_TIMEOUT = 5000, CACHE_DIR, VERSION_FILE, INSTALLED_PACKAGE_JSON, USER_CONFIG_DIR, USER_OPENCODE_CONFIG, USER_OPENCODE_CONFIG_JSONC;
6748
- var init_constants = __esm(() => {
6903
+ var init_constants3 = __esm(() => {
6749
6904
  init_shared();
6750
6905
  NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME2}/dist-tags`;
6751
- CACHE_DIR = getCacheDir();
6906
+ CACHE_DIR = getCacheDir2();
6752
6907
  VERSION_FILE = path3.join(CACHE_DIR, "version");
6753
6908
  INSTALLED_PACKAGE_JSON = path3.join(CACHE_DIR, "node_modules", PACKAGE_NAME2, "package.json");
6754
6909
  USER_CONFIG_DIR = getOpenCodeConfigDir({ binary: "opencode" });
@@ -6825,7 +6980,7 @@ function invalidateCache() {
6825
6980
  return invalidatePackage();
6826
6981
  }
6827
6982
  var init_cache = __esm(() => {
6828
- init_constants();
6983
+ init_constants3();
6829
6984
  init_logger();
6830
6985
  });
6831
6986
 
@@ -6897,6 +7052,7 @@ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on S
6897
7052
  const displayVersion = localDevVersion ?? cachedVersion;
6898
7053
  await showConfigErrorsIfAny(ctx);
6899
7054
  await showModelCacheWarningIfNeeded(ctx);
7055
+ await updateAndShowConnectedProvidersCacheStatus(ctx);
6900
7056
  if (localDevVersion) {
6901
7057
  if (showStartupToast) {
6902
7058
  showLocalDevToast(ctx, displayVersion, isSisyphusEnabled).catch(() => {});
@@ -6983,6 +7139,23 @@ async function showModelCacheWarningIfNeeded(ctx) {
6983
7139
  }).catch(() => {});
6984
7140
  log("[auto-update-checker] Model cache warning shown");
6985
7141
  }
7142
+ async function updateAndShowConnectedProvidersCacheStatus(ctx) {
7143
+ const hadCache = hasConnectedProvidersCache();
7144
+ updateConnectedProvidersCache(ctx.client).catch(() => {});
7145
+ if (!hadCache) {
7146
+ await ctx.client.tui.showToast({
7147
+ body: {
7148
+ title: "Connected Providers Cache",
7149
+ message: "Building provider cache for first time. Restart OpenCode for full model filtering.",
7150
+ variant: "info",
7151
+ duration: 8000
7152
+ }
7153
+ }).catch(() => {});
7154
+ log("[auto-update-checker] Connected providers cache toast shown (first run)");
7155
+ } else {
7156
+ log("[auto-update-checker] Connected providers cache exists, updating in background");
7157
+ }
7158
+ }
6986
7159
  async function showConfigErrorsIfAny(ctx) {
6987
7160
  const errors = getConfigLoadErrors();
6988
7161
  if (errors.length === 0)
@@ -7056,11 +7229,12 @@ var SISYPHUS_SPINNER;
7056
7229
  var init_auto_update_checker = __esm(() => {
7057
7230
  init_checker();
7058
7231
  init_cache();
7059
- init_constants();
7232
+ init_constants3();
7060
7233
  init_logger();
7061
7234
  init_config_errors();
7062
7235
  init_config_manager();
7063
7236
  init_model_availability();
7237
+ init_connected_providers_cache();
7064
7238
  init_checker();
7065
7239
  init_cache();
7066
7240
  SISYPHUS_SPINNER = ["\xB7", "\u2022", "\u25CF", "\u25CB", "\u25CC", "\u25E6", " "];
@@ -7295,7 +7469,7 @@ async function checkForUpdate(directory) {
7295
7469
  return { needsUpdate, currentVersion, latestVersion, isLocalDev: false, isPinned: pluginInfo.isPinned };
7296
7470
  }
7297
7471
  var init_checker = __esm(() => {
7298
- init_constants();
7472
+ init_constants3();
7299
7473
  init_logger();
7300
7474
  });
7301
7475
 
@@ -7901,7 +8075,7 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
7901
8075
  // package.json
7902
8076
  var package_default = {
7903
8077
  name: "oh-my-opencode",
7904
- version: "3.0.1",
8078
+ version: "3.1.0",
7905
8079
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
7906
8080
  main: "dist/index.js",
7907
8081
  types: "dist/index.d.ts",
@@ -7974,13 +8148,13 @@ var package_default = {
7974
8148
  typescript: "^5.7.3"
7975
8149
  },
7976
8150
  optionalDependencies: {
7977
- "oh-my-opencode-darwin-arm64": "3.0.1",
7978
- "oh-my-opencode-darwin-x64": "3.0.1",
7979
- "oh-my-opencode-linux-arm64": "3.0.1",
7980
- "oh-my-opencode-linux-arm64-musl": "3.0.1",
7981
- "oh-my-opencode-linux-x64": "3.0.1",
7982
- "oh-my-opencode-linux-x64-musl": "3.0.1",
7983
- "oh-my-opencode-windows-x64": "3.0.1"
8151
+ "oh-my-opencode-darwin-arm64": "3.1.0",
8152
+ "oh-my-opencode-darwin-x64": "3.1.0",
8153
+ "oh-my-opencode-linux-arm64": "3.1.0",
8154
+ "oh-my-opencode-linux-arm64-musl": "3.1.0",
8155
+ "oh-my-opencode-linux-x64": "3.1.0",
8156
+ "oh-my-opencode-linux-x64-musl": "3.1.0",
8157
+ "oh-my-opencode-windows-x64": "3.1.0"
7984
8158
  },
7985
8159
  trustedDependencies: [
7986
8160
  "@ast-grep/cli",
@@ -10642,14 +10816,14 @@ function getOpenCodeCheckDefinition() {
10642
10816
  }
10643
10817
 
10644
10818
  // src/cli/doctor/checks/plugin.ts
10645
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
10819
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
10646
10820
  init_shared();
10647
10821
  function detectConfigPath() {
10648
10822
  const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
10649
- if (existsSync7(paths.configJsonc)) {
10823
+ if (existsSync8(paths.configJsonc)) {
10650
10824
  return { path: paths.configJsonc, format: "jsonc" };
10651
10825
  }
10652
- if (existsSync7(paths.configJson)) {
10826
+ if (existsSync8(paths.configJson)) {
10653
10827
  return { path: paths.configJson, format: "json" };
10654
10828
  }
10655
10829
  return null;
@@ -10679,7 +10853,7 @@ function getPluginInfo() {
10679
10853
  };
10680
10854
  }
10681
10855
  try {
10682
- const content = readFileSync6(configInfo.path, "utf-8");
10856
+ const content = readFileSync7(configInfo.path, "utf-8");
10683
10857
  const config = parseJsonc(content);
10684
10858
  const plugins = config.plugin ?? [];
10685
10859
  const pluginEntry = findPluginEntry2(plugins);
@@ -10753,8 +10927,8 @@ function getPluginCheckDefinition() {
10753
10927
  }
10754
10928
 
10755
10929
  // src/cli/doctor/checks/config.ts
10756
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
10757
- import { join as join8 } from "path";
10930
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
10931
+ import { join as join10 } from "path";
10758
10932
  init_shared();
10759
10933
 
10760
10934
  // node_modules/zod/v4/classic/external.js
@@ -20841,10 +21015,10 @@ function _property(property, schema2, params) {
20841
21015
  ...normalizeParams(params)
20842
21016
  });
20843
21017
  }
20844
- function _mime(types2, params) {
21018
+ function _mime(types3, params) {
20845
21019
  return new $ZodCheckMimeType({
20846
21020
  check: "mime_type",
20847
- mime: types2,
21021
+ mime: types3,
20848
21022
  ...normalizeParams(params)
20849
21023
  });
20850
21024
  }
@@ -22754,7 +22928,7 @@ var ZodFile = /* @__PURE__ */ $constructor("ZodFile", (inst, def) => {
22754
22928
  ZodType.init(inst, def);
22755
22929
  inst.min = (size, params) => inst.check(_minSize(size, params));
22756
22930
  inst.max = (size, params) => inst.check(_maxSize(size, params));
22757
- inst.mime = (types2, params) => inst.check(_mime(Array.isArray(types2) ? types2 : [types2], params));
22931
+ inst.mime = (types3, params) => inst.check(_mime(Array.isArray(types3) ? types3 : [types3], params));
22758
22932
  });
22759
22933
  function file(params) {
22760
22934
  return _file(ZodFile, params);
@@ -23102,6 +23276,7 @@ var BuiltinAgentNameSchema = exports_external.enum([
23102
23276
  ]);
23103
23277
  var BuiltinSkillNameSchema = exports_external.enum([
23104
23278
  "playwright",
23279
+ "agent-browser",
23105
23280
  "frontend-ui-ux",
23106
23281
  "git-master"
23107
23282
  ]);
@@ -23143,12 +23318,14 @@ var HookNameSchema = exports_external.enum([
23143
23318
  "interactive-bash-session",
23144
23319
  "thinking-block-validator",
23145
23320
  "ralph-loop",
23321
+ "category-skill-reminder",
23146
23322
  "compaction-context-injector",
23147
23323
  "claude-code-hooks",
23148
23324
  "auto-slash-command",
23149
23325
  "edit-error-recovery",
23150
23326
  "delegate-task-retry",
23151
23327
  "prometheus-md-only",
23328
+ "sisyphus-junior-notepad",
23152
23329
  "start-work",
23153
23330
  "atlas"
23154
23331
  ]);
@@ -23320,6 +23497,24 @@ var GitMasterConfigSchema = exports_external.object({
23320
23497
  commit_footer: exports_external.boolean().default(true),
23321
23498
  include_co_authored_by: exports_external.boolean().default(true)
23322
23499
  });
23500
+ var BrowserAutomationProviderSchema = exports_external.enum(["playwright", "agent-browser"]);
23501
+ var BrowserAutomationConfigSchema = exports_external.object({
23502
+ provider: BrowserAutomationProviderSchema.default("playwright")
23503
+ });
23504
+ var TmuxLayoutSchema = exports_external.enum([
23505
+ "main-horizontal",
23506
+ "main-vertical",
23507
+ "tiled",
23508
+ "even-horizontal",
23509
+ "even-vertical"
23510
+ ]);
23511
+ var TmuxConfigSchema = exports_external.object({
23512
+ enabled: exports_external.boolean().default(false),
23513
+ layout: TmuxLayoutSchema.default("main-vertical"),
23514
+ main_pane_size: exports_external.number().min(20).max(80).default(60),
23515
+ main_pane_min_width: exports_external.number().min(40).default(120),
23516
+ agent_pane_min_width: exports_external.number().min(20).default(40)
23517
+ });
23323
23518
  var OhMyOpenCodeConfigSchema = exports_external.object({
23324
23519
  $schema: exports_external.string().optional(),
23325
23520
  disabled_mcps: exports_external.array(AnyMcpNameSchema).optional(),
@@ -23338,12 +23533,14 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
23338
23533
  ralph_loop: RalphLoopConfigSchema.optional(),
23339
23534
  background_task: BackgroundTaskConfigSchema.optional(),
23340
23535
  notification: NotificationConfigSchema.optional(),
23341
- git_master: GitMasterConfigSchema.optional()
23536
+ git_master: GitMasterConfigSchema.optional(),
23537
+ browser_automation_engine: BrowserAutomationConfigSchema.optional(),
23538
+ tmux: TmuxConfigSchema.optional()
23342
23539
  });
23343
23540
  // src/cli/doctor/checks/config.ts
23344
23541
  var USER_CONFIG_DIR2 = getOpenCodeConfigDir({ binary: "opencode" });
23345
- var USER_CONFIG_BASE = join8(USER_CONFIG_DIR2, `${PACKAGE_NAME3}`);
23346
- var PROJECT_CONFIG_BASE = join8(process.cwd(), ".opencode", PACKAGE_NAME3);
23542
+ var USER_CONFIG_BASE = join10(USER_CONFIG_DIR2, `${PACKAGE_NAME3}`);
23543
+ var PROJECT_CONFIG_BASE = join10(process.cwd(), ".opencode", PACKAGE_NAME3);
23347
23544
  function findConfigPath() {
23348
23545
  const projectDetected = detectConfigFile(PROJECT_CONFIG_BASE);
23349
23546
  if (projectDetected.format !== "none") {
@@ -23357,7 +23554,7 @@ function findConfigPath() {
23357
23554
  }
23358
23555
  function validateConfig(configPath) {
23359
23556
  try {
23360
- const content = readFileSync7(configPath, "utf-8");
23557
+ const content = readFileSync8(configPath, "utf-8");
23361
23558
  const rawConfig = parseJsonc(content);
23362
23559
  const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
23363
23560
  if (!result.success) {
@@ -23383,7 +23580,7 @@ function getConfigInfo() {
23383
23580
  errors: []
23384
23581
  };
23385
23582
  }
23386
- if (!existsSync8(configPath.path)) {
23583
+ if (!existsSync9(configPath.path)) {
23387
23584
  return {
23388
23585
  exists: false,
23389
23586
  path: configPath.path,
@@ -23440,24 +23637,24 @@ function getConfigCheckDefinition() {
23440
23637
  }
23441
23638
 
23442
23639
  // src/cli/doctor/checks/model-resolution.ts
23443
- import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
23640
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
23444
23641
  init_shared();
23445
23642
  init_model_requirements();
23446
- import { homedir as homedir6 } from "os";
23447
- import { join as join9 } from "path";
23643
+ import { homedir as homedir5 } from "os";
23644
+ import { join as join11 } from "path";
23448
23645
  function getOpenCodeCacheDir2() {
23449
23646
  const xdgCache = process.env.XDG_CACHE_HOME;
23450
23647
  if (xdgCache)
23451
- return join9(xdgCache, "opencode");
23452
- return join9(homedir6(), ".cache", "opencode");
23648
+ return join11(xdgCache, "opencode");
23649
+ return join11(homedir5(), ".cache", "opencode");
23453
23650
  }
23454
23651
  function loadAvailableModels() {
23455
- const cacheFile = join9(getOpenCodeCacheDir2(), "models.json");
23456
- if (!existsSync9(cacheFile)) {
23652
+ const cacheFile = join11(getOpenCodeCacheDir2(), "models.json");
23653
+ if (!existsSync10(cacheFile)) {
23457
23654
  return { providers: [], modelCount: 0, cacheExists: false };
23458
23655
  }
23459
23656
  try {
23460
- const content = readFileSync8(cacheFile, "utf-8");
23657
+ const content = readFileSync9(cacheFile, "utf-8");
23461
23658
  const data = JSON.parse(content);
23462
23659
  const providers = Object.keys(data);
23463
23660
  let modelCount = 0;
@@ -23473,14 +23670,14 @@ function loadAvailableModels() {
23473
23670
  }
23474
23671
  }
23475
23672
  var PACKAGE_NAME4 = "oh-my-opencode";
23476
- var USER_CONFIG_DIR3 = join9(homedir6(), ".config", "opencode");
23477
- var USER_CONFIG_BASE2 = join9(USER_CONFIG_DIR3, PACKAGE_NAME4);
23478
- var PROJECT_CONFIG_BASE2 = join9(process.cwd(), ".opencode", PACKAGE_NAME4);
23673
+ var USER_CONFIG_DIR3 = join11(homedir5(), ".config", "opencode");
23674
+ var USER_CONFIG_BASE2 = join11(USER_CONFIG_DIR3, PACKAGE_NAME4);
23675
+ var PROJECT_CONFIG_BASE2 = join11(process.cwd(), ".opencode", PACKAGE_NAME4);
23479
23676
  function loadConfig() {
23480
23677
  const projectDetected = detectConfigFile(PROJECT_CONFIG_BASE2);
23481
23678
  if (projectDetected.format !== "none") {
23482
23679
  try {
23483
- const content = readFileSync8(projectDetected.path, "utf-8");
23680
+ const content = readFileSync9(projectDetected.path, "utf-8");
23484
23681
  return parseJsonc(content);
23485
23682
  } catch {
23486
23683
  return null;
@@ -23489,7 +23686,7 @@ function loadConfig() {
23489
23686
  const userDetected = detectConfigFile(USER_CONFIG_BASE2);
23490
23687
  if (userDetected.format !== "none") {
23491
23688
  try {
23492
- const content = readFileSync8(userDetected.path, "utf-8");
23689
+ const content = readFileSync9(userDetected.path, "utf-8");
23493
23690
  return parseJsonc(content);
23494
23691
  } catch {
23495
23692
  return null;
@@ -23555,9 +23752,11 @@ function buildDetailsArray(info, available) {
23555
23752
  details.push("\u2550\u2550\u2550 Available Models (from cache) \u2550\u2550\u2550");
23556
23753
  details.push("");
23557
23754
  if (available.cacheExists) {
23558
- details.push(` Providers: ${available.providers.length} (${available.providers.slice(0, 8).join(", ")}${available.providers.length > 8 ? "..." : ""})`);
23755
+ details.push(` Providers in cache: ${available.providers.length}`);
23756
+ details.push(` Sample: ${available.providers.slice(0, 6).join(", ")}${available.providers.length > 6 ? "..." : ""}`);
23559
23757
  details.push(` Total models: ${available.modelCount}`);
23560
23758
  details.push(` Cache: ~/.cache/opencode/models.json`);
23759
+ details.push(` \u2139 Runtime: only connected providers used`);
23561
23760
  details.push(` Refresh: opencode models --refresh`);
23562
23761
  } else {
23563
23762
  details.push(" \u26A0 Cache not found. Run 'opencode' to populate.");
@@ -23611,23 +23810,23 @@ function getModelResolutionCheckDefinition() {
23611
23810
  }
23612
23811
 
23613
23812
  // src/cli/doctor/checks/auth.ts
23614
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
23615
- import { join as join10 } from "path";
23813
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
23814
+ import { join as join12 } from "path";
23616
23815
  init_shared();
23617
23816
  var OPENCODE_CONFIG_DIR = getOpenCodeConfigDir({ binary: "opencode" });
23618
- var OPENCODE_JSON = join10(OPENCODE_CONFIG_DIR, "opencode.json");
23619
- var OPENCODE_JSONC = join10(OPENCODE_CONFIG_DIR, "opencode.jsonc");
23817
+ var OPENCODE_JSON = join12(OPENCODE_CONFIG_DIR, "opencode.json");
23818
+ var OPENCODE_JSONC = join12(OPENCODE_CONFIG_DIR, "opencode.jsonc");
23620
23819
  var AUTH_PLUGINS = {
23621
23820
  anthropic: { plugin: "builtin", name: "Anthropic (Claude)" },
23622
23821
  openai: { plugin: "opencode-openai-codex-auth", name: "OpenAI (ChatGPT)" },
23623
23822
  google: { plugin: "opencode-antigravity-auth", name: "Google (Gemini)" }
23624
23823
  };
23625
23824
  function getOpenCodeConfig() {
23626
- const configPath = existsSync10(OPENCODE_JSONC) ? OPENCODE_JSONC : OPENCODE_JSON;
23627
- if (!existsSync10(configPath))
23825
+ const configPath = existsSync11(OPENCODE_JSONC) ? OPENCODE_JSONC : OPENCODE_JSON;
23826
+ if (!existsSync11(configPath))
23628
23827
  return null;
23629
23828
  try {
23630
- const content = readFileSync9(configPath, "utf-8");
23829
+ const content = readFileSync10(configPath, "utf-8");
23631
23830
  return parseJsonc(content);
23632
23831
  } catch {
23633
23832
  return null;
@@ -23767,15 +23966,15 @@ async function checkAstGrepNapi() {
23767
23966
  path: null
23768
23967
  };
23769
23968
  } catch {
23770
- const { existsSync: existsSync11 } = await import("fs");
23771
- const { join: join11 } = await import("path");
23772
- const { homedir: homedir7 } = await import("os");
23969
+ const { existsSync: existsSync12 } = await import("fs");
23970
+ const { join: join13 } = await import("path");
23971
+ const { homedir: homedir6 } = await import("os");
23773
23972
  const pathsToCheck = [
23774
- join11(homedir7(), ".config", "opencode", "node_modules", "@ast-grep", "napi"),
23775
- join11(process.cwd(), "node_modules", "@ast-grep", "napi")
23973
+ join13(homedir6(), ".config", "opencode", "node_modules", "@ast-grep", "napi"),
23974
+ join13(process.cwd(), "node_modules", "@ast-grep", "napi")
23776
23975
  ];
23777
23976
  for (const napiPath of pathsToCheck) {
23778
- if (existsSync11(napiPath)) {
23977
+ if (existsSync12(napiPath)) {
23779
23978
  return {
23780
23979
  name: "AST-Grep NAPI",
23781
23980
  required: false,
@@ -24004,15 +24203,15 @@ function getGhCliCheckDefinition() {
24004
24203
  }
24005
24204
 
24006
24205
  // src/tools/lsp/config.ts
24007
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
24008
- import { join as join11 } from "path";
24206
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
24207
+ import { join as join13 } from "path";
24009
24208
  init_shared();
24010
24209
  function isServerInstalled(command) {
24011
24210
  if (command.length === 0)
24012
24211
  return false;
24013
24212
  const cmd = command[0];
24014
24213
  if (cmd.includes("/") || cmd.includes("\\")) {
24015
- if (existsSync11(cmd))
24214
+ if (existsSync12(cmd))
24016
24215
  return true;
24017
24216
  }
24018
24217
  const isWindows = process.platform === "win32";
@@ -24034,23 +24233,23 @@ function isServerInstalled(command) {
24034
24233
  const paths = pathEnv.split(pathSeparator);
24035
24234
  for (const p2 of paths) {
24036
24235
  for (const suffix of exts) {
24037
- if (existsSync11(join11(p2, cmd + suffix))) {
24236
+ if (existsSync12(join13(p2, cmd + suffix))) {
24038
24237
  return true;
24039
24238
  }
24040
24239
  }
24041
24240
  }
24042
24241
  const cwd = process.cwd();
24043
24242
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
24044
- const dataDir = join11(getDataDir(), "opencode");
24243
+ const dataDir = join13(getDataDir(), "opencode");
24045
24244
  const additionalBases = [
24046
- join11(cwd, "node_modules", ".bin"),
24047
- join11(configDir, "bin"),
24048
- join11(configDir, "node_modules", ".bin"),
24049
- join11(dataDir, "bin")
24245
+ join13(cwd, "node_modules", ".bin"),
24246
+ join13(configDir, "bin"),
24247
+ join13(configDir, "node_modules", ".bin"),
24248
+ join13(dataDir, "bin")
24050
24249
  ];
24051
24250
  for (const base of additionalBases) {
24052
24251
  for (const suffix of exts) {
24053
- if (existsSync11(join11(base, cmd + suffix))) {
24252
+ if (existsSync12(join13(base, cmd + suffix))) {
24054
24253
  return true;
24055
24254
  }
24056
24255
  }
@@ -24123,23 +24322,23 @@ function getLspCheckDefinition() {
24123
24322
  }
24124
24323
 
24125
24324
  // src/cli/doctor/checks/mcp.ts
24126
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
24127
- import { homedir as homedir7 } from "os";
24128
- import { join as join12 } from "path";
24325
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
24326
+ import { homedir as homedir6 } from "os";
24327
+ import { join as join14 } from "path";
24129
24328
  init_shared();
24130
24329
  var BUILTIN_MCP_SERVERS = ["context7", "grep_app"];
24131
24330
  var MCP_CONFIG_PATHS = [
24132
- join12(homedir7(), ".claude", ".mcp.json"),
24133
- join12(process.cwd(), ".mcp.json"),
24134
- join12(process.cwd(), ".claude", ".mcp.json")
24331
+ join14(homedir6(), ".claude", ".mcp.json"),
24332
+ join14(process.cwd(), ".mcp.json"),
24333
+ join14(process.cwd(), ".claude", ".mcp.json")
24135
24334
  ];
24136
24335
  function loadUserMcpConfig() {
24137
24336
  const servers = {};
24138
24337
  for (const configPath of MCP_CONFIG_PATHS) {
24139
- if (!existsSync12(configPath))
24338
+ if (!existsSync13(configPath))
24140
24339
  continue;
24141
24340
  try {
24142
- const content = readFileSync11(configPath, "utf-8");
24341
+ const content = readFileSync12(configPath, "utf-8");
24143
24342
  const config2 = parseJsonc(content);
24144
24343
  if (config2.mcpServers) {
24145
24344
  Object.assign(servers, config2.mcpServers);