oh-my-opencode 3.15.0 → 3.15.2

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
@@ -17496,7 +17496,7 @@ function getOpenCodeConfigPaths(options) {
17496
17496
  configJson: join6(configDir, "opencode.json"),
17497
17497
  configJsonc: join6(configDir, "opencode.jsonc"),
17498
17498
  packageJson: join6(configDir, "package.json"),
17499
- omoConfig: join6(configDir, "oh-my-opencode.json")
17499
+ omoConfig: join6(configDir, `${CONFIG_BASENAME}.json`)
17500
17500
  };
17501
17501
  }
17502
17502
  // src/shared/opencode-version.ts
@@ -60911,8 +60911,6 @@ function getMessageDir(sessionID) {
60911
60911
  return null;
60912
60912
  if (/[/\\]|\.\./.test(sessionID))
60913
60913
  return null;
60914
- if (isSqliteBackend())
60915
- return null;
60916
60914
  if (!existsSync12(MESSAGE_STORAGE))
60917
60915
  return null;
60918
60916
  const directPath = join14(MESSAGE_STORAGE, sessionID);
@@ -62296,11 +62294,11 @@ function getPluginsBaseDir() {
62296
62294
  }
62297
62295
  return join18(homedir6(), ".claude", "plugins");
62298
62296
  }
62299
- function getInstalledPluginsPath() {
62300
- return join18(getPluginsBaseDir(), "installed_plugins.json");
62297
+ function getInstalledPluginsPath(pluginsBaseDir) {
62298
+ return join18(pluginsBaseDir ?? getPluginsBaseDir(), "installed_plugins.json");
62301
62299
  }
62302
- function loadInstalledPlugins() {
62303
- const dbPath = getInstalledPluginsPath();
62300
+ function loadInstalledPlugins(pluginsBaseDir) {
62301
+ const dbPath = getInstalledPluginsPath(pluginsBaseDir);
62304
62302
  if (!existsSync14(dbPath)) {
62305
62303
  return null;
62306
62304
  }
@@ -62404,7 +62402,8 @@ function extractPluginEntries(db) {
62404
62402
  return Object.entries(db.plugins).map(([key, installations]) => [key, installations[0]]);
62405
62403
  }
62406
62404
  function discoverInstalledPlugins(options) {
62407
- const db = loadInstalledPlugins();
62405
+ const pluginsBaseDir = options?.pluginsHomeOverride ?? getPluginsBaseDir();
62406
+ const db = loadInstalledPlugins(pluginsBaseDir);
62408
62407
  const settings = loadClaudeSettings();
62409
62408
  const plugins = [];
62410
62409
  const errors = [];
@@ -62413,6 +62412,7 @@ function discoverInstalledPlugins(options) {
62413
62412
  }
62414
62413
  const settingsEnabledPlugins = settings?.enabledPlugins;
62415
62414
  const overrideEnabledPlugins = options?.enabledPluginsOverride;
62415
+ const pluginManifestLoader = options?.loadPluginManifestOverride ?? loadPluginManifest;
62416
62416
  for (const [pluginKey, installation] of extractPluginEntries(db)) {
62417
62417
  if (!installation)
62418
62418
  continue;
@@ -62429,7 +62429,7 @@ function discoverInstalledPlugins(options) {
62429
62429
  });
62430
62430
  continue;
62431
62431
  }
62432
- const manifest = loadPluginManifest(installPath);
62432
+ const manifest = pluginManifestLoader(installPath);
62433
62433
  const pluginName = manifest?.name || derivePluginNameFromKey(pluginKey);
62434
62434
  const loadedPlugin = {
62435
62435
  name: pluginName,
@@ -62930,11 +62930,21 @@ function loadPluginHooksConfigs(plugins) {
62930
62930
  }
62931
62931
 
62932
62932
  // src/features/claude-code-plugin-loader/loader.ts
62933
+ var cachedPluginComponentsByKey = new Map;
62934
+ function clonePluginComponentsResult(result) {
62935
+ return structuredClone(result);
62936
+ }
62933
62937
  function isClaudeCodePluginsDisabled() {
62934
62938
  const disableFlag = process.env.OPENCODE_DISABLE_CLAUDE_CODE;
62935
62939
  const disablePluginsFlag = process.env.OPENCODE_DISABLE_CLAUDE_CODE_PLUGINS;
62936
62940
  return disableFlag === "true" || disableFlag === "1" || disablePluginsFlag === "true" || disablePluginsFlag === "1";
62937
62941
  }
62942
+ function getPluginComponentsCacheKey(options) {
62943
+ const overrideEntries = Object.entries(options?.enabledPluginsOverride ?? {}).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
62944
+ return JSON.stringify({
62945
+ enabledPluginsOverride: overrideEntries
62946
+ });
62947
+ }
62938
62948
  async function loadAllPluginComponents(options) {
62939
62949
  if (isClaudeCodePluginsDisabled()) {
62940
62950
  log("Claude Code plugin loading disabled via OPENCODE_DISABLE_CLAUDE_CODE env var");
@@ -62948,6 +62958,11 @@ async function loadAllPluginComponents(options) {
62948
62958
  errors: []
62949
62959
  };
62950
62960
  }
62961
+ const cacheKey = getPluginComponentsCacheKey(options);
62962
+ const cachedPluginComponents = cachedPluginComponentsByKey.get(cacheKey);
62963
+ if (cachedPluginComponents) {
62964
+ return clonePluginComponentsResult(cachedPluginComponents);
62965
+ }
62951
62966
  const { plugins, errors } = discoverInstalledPlugins(options);
62952
62967
  const [commands, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
62953
62968
  Promise.resolve(loadPluginCommands(plugins)),
@@ -62957,7 +62972,7 @@ async function loadAllPluginComponents(options) {
62957
62972
  Promise.resolve(loadPluginHooksConfigs(plugins))
62958
62973
  ]);
62959
62974
  log(`Loaded ${plugins.length} plugins with ${Object.keys(commands).length} commands, ${Object.keys(skills).length} skills, ${Object.keys(agents).length} agents, ${Object.keys(mcpServers).length} MCP servers`);
62960
- return {
62975
+ const result = {
62961
62976
  commands,
62962
62977
  skills,
62963
62978
  agents,
@@ -62966,6 +62981,8 @@ async function loadAllPluginComponents(options) {
62966
62981
  plugins,
62967
62982
  errors
62968
62983
  };
62984
+ cachedPluginComponentsByKey.set(cacheKey, clonePluginComponentsResult(result));
62985
+ return clonePluginComponentsResult(result);
62969
62986
  }
62970
62987
  // src/shared/plugin-command-discovery.ts
62971
62988
  function discoverPluginCommandDefinitions(options) {
@@ -63600,6 +63617,12 @@ function startCountdown(args) {
63600
63617
  }
63601
63618
 
63602
63619
  // src/hooks/todo-continuation-enforcer/idle-event.ts
63620
+ function shouldAllowActivityProgress(modelID) {
63621
+ if (!modelID) {
63622
+ return false;
63623
+ }
63624
+ return !modelID.toLowerCase().includes("codex");
63625
+ }
63603
63626
  async function handleSessionIdle(args) {
63604
63627
  const {
63605
63628
  ctx,
@@ -63731,7 +63754,7 @@ async function handleSessionIdle(args) {
63731
63754
  log(`[${HOOK_NAME}] Skipped: continuation stopped for session`, { sessionID });
63732
63755
  return;
63733
63756
  }
63734
- const progressUpdate = sessionStateStore.trackContinuationProgress(sessionID, incompleteCount, todos);
63757
+ const progressUpdate = sessionStateStore.trackContinuationProgress(sessionID, incompleteCount, todos, { allowActivityProgress: shouldAllowActivityProgress(resolvedInfo?.model?.modelID) });
63735
63758
  if (shouldStopForStagnation({ sessionID, incompleteCount, progressUpdate })) {
63736
63759
  return;
63737
63760
  }
@@ -63770,6 +63793,7 @@ function handleNonIdleEvent(args) {
63770
63793
  if (state2) {
63771
63794
  state2.abortDetectedAt = undefined;
63772
63795
  state2.wasCancelled = false;
63796
+ sessionStateStore.recordActivity(sessionID);
63773
63797
  }
63774
63798
  sessionStateStore.cancelCountdown(sessionID);
63775
63799
  return;
@@ -63779,6 +63803,7 @@ function handleNonIdleEvent(args) {
63779
63803
  if (state2) {
63780
63804
  state2.abortDetectedAt = undefined;
63781
63805
  state2.wasCancelled = false;
63806
+ sessionStateStore.recordActivity(sessionID);
63782
63807
  }
63783
63808
  sessionStateStore.cancelCountdown(sessionID);
63784
63809
  return;
@@ -63792,8 +63817,10 @@ function handleNonIdleEvent(args) {
63792
63817
  const targetSessionID = sessionID ?? legacySessionID;
63793
63818
  if (targetSessionID) {
63794
63819
  const state2 = sessionStateStore.getExistingState(targetSessionID);
63795
- if (state2)
63820
+ if (state2) {
63796
63821
  state2.abortDetectedAt = undefined;
63822
+ sessionStateStore.recordActivity(targetSessionID);
63823
+ }
63797
63824
  sessionStateStore.cancelCountdown(targetSessionID);
63798
63825
  }
63799
63826
  return;
@@ -63805,6 +63832,7 @@ function handleNonIdleEvent(args) {
63805
63832
  if (state2) {
63806
63833
  state2.abortDetectedAt = undefined;
63807
63834
  state2.wasCancelled = false;
63835
+ sessionStateStore.recordActivity(sessionID);
63808
63836
  }
63809
63837
  sessionStateStore.cancelCountdown(sessionID);
63810
63838
  }
@@ -63817,6 +63845,7 @@ function handleNonIdleEvent(args) {
63817
63845
  if (state2) {
63818
63846
  state2.abortDetectedAt = undefined;
63819
63847
  state2.wasCancelled = false;
63848
+ sessionStateStore.recordActivity(sessionID);
63820
63849
  }
63821
63850
  sessionStateStore.cancelCountdown(sessionID);
63822
63851
  }
@@ -63953,7 +63982,8 @@ function createSessionStateStore() {
63953
63982
  };
63954
63983
  const trackedSession = {
63955
63984
  state: rawState,
63956
- lastAccessedAt: Date.now()
63985
+ lastAccessedAt: Date.now(),
63986
+ activitySignalCount: 0
63957
63987
  };
63958
63988
  sessions.set(sessionID, trackedSession);
63959
63989
  return trackedSession;
@@ -63969,15 +63999,21 @@ function createSessionStateStore() {
63969
63999
  }
63970
64000
  return;
63971
64001
  }
63972
- function trackContinuationProgress(sessionID, incompleteCount, todos) {
64002
+ function recordActivity(sessionID) {
64003
+ const trackedSession = getTrackedSession(sessionID);
64004
+ trackedSession.activitySignalCount += 1;
64005
+ }
64006
+ function trackContinuationProgress(sessionID, incompleteCount, todos, options = {}) {
63973
64007
  const trackedSession = getTrackedSession(sessionID);
63974
64008
  const state2 = trackedSession.state;
63975
64009
  const previousIncompleteCount = state2.lastIncompleteCount;
63976
64010
  const previousStagnationCount = state2.stagnationCount;
63977
64011
  const currentCompletedCount = todos?.filter((todo) => todo.status === "completed").length;
63978
64012
  const currentTodoSnapshot = todos ? getTodoSnapshot(todos) : undefined;
64013
+ const currentActivitySignalCount = trackedSession.activitySignalCount;
63979
64014
  const hasCompletedMoreTodos = currentCompletedCount !== undefined && trackedSession.lastCompletedCount !== undefined && currentCompletedCount > trackedSession.lastCompletedCount;
63980
64015
  const hasTodoSnapshotChanged = currentTodoSnapshot !== undefined && trackedSession.lastTodoSnapshot !== undefined && currentTodoSnapshot !== trackedSession.lastTodoSnapshot;
64016
+ const hasObservedExternalActivity = options.allowActivityProgress === true && trackedSession.lastObservedActivitySignalCount !== undefined && currentActivitySignalCount > trackedSession.lastObservedActivitySignalCount;
63981
64017
  const hadSuccessfulInjectionAwaitingProgressCheck = state2.awaitingPostInjectionProgressCheck === true;
63982
64018
  state2.lastIncompleteCount = incompleteCount;
63983
64019
  if (currentCompletedCount !== undefined) {
@@ -63986,6 +64022,7 @@ function createSessionStateStore() {
63986
64022
  if (currentTodoSnapshot !== undefined) {
63987
64023
  trackedSession.lastTodoSnapshot = currentTodoSnapshot;
63988
64024
  }
64025
+ trackedSession.lastObservedActivitySignalCount = currentActivitySignalCount;
63989
64026
  if (previousIncompleteCount === undefined) {
63990
64027
  state2.stagnationCount = 0;
63991
64028
  return {
@@ -63996,7 +64033,7 @@ function createSessionStateStore() {
63996
64033
  progressSource: "none"
63997
64034
  };
63998
64035
  }
63999
- const progressSource = incompleteCount < previousIncompleteCount || hasCompletedMoreTodos || hasTodoSnapshotChanged ? "todo" : "none";
64036
+ const progressSource = incompleteCount < previousIncompleteCount || hasCompletedMoreTodos || hasTodoSnapshotChanged ? "todo" : hasObservedExternalActivity ? "activity" : "none";
64000
64037
  if (progressSource !== "none") {
64001
64038
  state2.stagnationCount = 0;
64002
64039
  state2.awaitingPostInjectionProgressCheck = false;
@@ -64038,6 +64075,8 @@ function createSessionStateStore() {
64038
64075
  state2.awaitingPostInjectionProgressCheck = false;
64039
64076
  trackedSession.lastCompletedCount = undefined;
64040
64077
  trackedSession.lastTodoSnapshot = undefined;
64078
+ trackedSession.activitySignalCount = 0;
64079
+ trackedSession.lastObservedActivitySignalCount = undefined;
64041
64080
  }
64042
64081
  function cancelCountdown(sessionID) {
64043
64082
  const tracked = sessions.get(sessionID);
@@ -64074,6 +64113,7 @@ function createSessionStateStore() {
64074
64113
  return {
64075
64114
  getState,
64076
64115
  getExistingState,
64116
+ recordActivity,
64077
64117
  trackContinuationProgress,
64078
64118
  resetContinuationProgress,
64079
64119
  cancelCountdown,
@@ -82148,11 +82188,13 @@ async function applyFallbackToChatMessage(params) {
82148
82188
  var RETRYABLE_ERROR_NAMES = new Set([
82149
82189
  "providermodelnotfounderror",
82150
82190
  "ratelimiterror",
82151
- "quotaexceedederror",
82152
- "insufficientcreditserror",
82153
82191
  "modelunavailableerror",
82154
82192
  "providerconnectionerror",
82155
- "authenticationerror",
82193
+ "authenticationerror"
82194
+ ]);
82195
+ var STOP_ERROR_NAMES = new Set([
82196
+ "quotaexceedederror",
82197
+ "insufficientcreditserror",
82156
82198
  "freeusagelimiterror"
82157
82199
  ]);
82158
82200
  var NON_RETRYABLE_ERROR_NAMES = new Set([
@@ -82180,6 +82222,7 @@ var RETRYABLE_MESSAGE_PATTERNS = [
82180
82222
  "over limit",
82181
82223
  "overloaded",
82182
82224
  "bad gateway",
82225
+ "bad request",
82183
82226
  "unknown provider",
82184
82227
  "provider not found",
82185
82228
  "model_not_supported",
@@ -82223,6 +82266,9 @@ function isRetryableModelError(error48) {
82223
82266
  if (NON_RETRYABLE_ERROR_NAMES.has(errorNameLower)) {
82224
82267
  return false;
82225
82268
  }
82269
+ if (STOP_ERROR_NAMES.has(errorNameLower)) {
82270
+ return false;
82271
+ }
82226
82272
  if (RETRYABLE_ERROR_NAMES.has(errorNameLower)) {
82227
82273
  return true;
82228
82274
  }
@@ -82503,13 +82549,15 @@ init_logger();
82503
82549
  import { existsSync as existsSync39 } from "fs";
82504
82550
  import { join as join42 } from "path";
82505
82551
  var CONFIG_CACHE_TTL_MS2 = 30000;
82506
- var USER_CONFIG_PATH = join42(getOpenCodeConfigDir({ binary: "opencode" }), "opencode-cc-plugin.json");
82507
82552
  var configCache2 = new Map;
82553
+ function getUserConfigPath() {
82554
+ return join42(getOpenCodeConfigDir({ binary: "opencode" }), "opencode-cc-plugin.json");
82555
+ }
82508
82556
  function getProjectConfigPath() {
82509
82557
  return join42(process.cwd(), ".opencode", "opencode-cc-plugin.json");
82510
82558
  }
82511
82559
  function getCacheKey2() {
82512
- return process.cwd();
82560
+ return `${process.cwd()}::${getUserConfigPath()}`;
82513
82561
  }
82514
82562
  function getCachedConfig2(cacheKey) {
82515
82563
  const cachedEntry = configCache2.get(cacheKey);
@@ -82556,7 +82604,7 @@ async function loadPluginExtendedConfig() {
82556
82604
  if (cachedConfig) {
82557
82605
  return cachedConfig;
82558
82606
  }
82559
- const userConfig = await loadConfigFromPath(USER_CONFIG_PATH);
82607
+ const userConfig = await loadConfigFromPath(getUserConfigPath());
82560
82608
  const projectConfig = await loadConfigFromPath(getProjectConfigPath());
82561
82609
  const merged = {
82562
82610
  disabledHooks: mergeDisabledHooks(userConfig?.disabledHooks, projectConfig?.disabledHooks)
@@ -84390,36 +84438,44 @@ import { fileURLToPath as fileURLToPath2 } from "url";
84390
84438
  // src/hooks/auto-update-checker/constants.ts
84391
84439
  import * as path5 from "path";
84392
84440
  import * as os4 from "os";
84393
- var PACKAGE_NAME = "oh-my-opencode";
84441
+ var PACKAGE_NAME = "oh-my-openagent";
84394
84442
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
84395
84443
  var NPM_FETCH_TIMEOUT = 5000;
84396
- var CACHE_DIR = getOpenCodeCacheDir();
84397
- var VERSION_FILE = path5.join(CACHE_DIR, "version");
84444
+ var CACHE_ROOT_DIR = getOpenCodeCacheDir();
84445
+ var CACHE_DIR = path5.join(CACHE_ROOT_DIR, "packages");
84446
+ var VERSION_FILE = path5.join(CACHE_ROOT_DIR, "version");
84398
84447
  function getWindowsAppdataDir2() {
84399
84448
  if (process.platform !== "win32")
84400
84449
  return null;
84401
84450
  return process.env.APPDATA ?? path5.join(os4.homedir(), "AppData", "Roaming");
84402
84451
  }
84403
- var USER_CONFIG_DIR = getOpenCodeConfigDir({ binary: "opencode" });
84404
- var USER_OPENCODE_CONFIG = path5.join(USER_CONFIG_DIR, "opencode.json");
84405
- var USER_OPENCODE_CONFIG_JSONC = path5.join(USER_CONFIG_DIR, "opencode.jsonc");
84452
+ function getUserConfigDir() {
84453
+ return getOpenCodeConfigDir({ binary: "opencode" });
84454
+ }
84455
+ function getUserOpencodeConfig() {
84456
+ return path5.join(getUserConfigDir(), "opencode.json");
84457
+ }
84458
+ function getUserOpencodeConfigJsonc() {
84459
+ return path5.join(getUserConfigDir(), "opencode.jsonc");
84460
+ }
84406
84461
  var INSTALLED_PACKAGE_JSON = path5.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
84407
84462
 
84408
84463
  // src/hooks/auto-update-checker/checker/config-paths.ts
84409
84464
  import * as os5 from "os";
84410
84465
  import * as path6 from "path";
84411
84466
  function getConfigPaths2(directory) {
84467
+ const userConfigDir = getUserConfigDir();
84412
84468
  const paths = [
84413
84469
  path6.join(directory, ".opencode", "opencode.json"),
84414
84470
  path6.join(directory, ".opencode", "opencode.jsonc"),
84415
- USER_OPENCODE_CONFIG,
84416
- USER_OPENCODE_CONFIG_JSONC
84471
+ getUserOpencodeConfig(),
84472
+ getUserOpencodeConfigJsonc()
84417
84473
  ];
84418
84474
  if (process.platform === "win32") {
84419
84475
  const crossPlatformDir = path6.join(os5.homedir(), ".config");
84420
84476
  const appdataDir = getWindowsAppdataDir2();
84421
84477
  if (appdataDir) {
84422
- const alternateDir = USER_CONFIG_DIR === crossPlatformDir ? appdataDir : crossPlatformDir;
84478
+ const alternateDir = userConfigDir === crossPlatformDir ? appdataDir : crossPlatformDir;
84423
84479
  const alternateConfig = path6.join(alternateDir, "opencode", "opencode.json");
84424
84480
  const alternateConfigJsonc = path6.join(alternateDir, "opencode", "opencode.jsonc");
84425
84481
  if (!paths.includes(alternateConfig)) {
@@ -84642,11 +84698,28 @@ function getIntentVersion(pluginInfo) {
84642
84698
  }
84643
84699
  return pluginInfo.pinnedVersion;
84644
84700
  }
84701
+ function writeCachePackageJson(cachePackageJsonPath, pkgJson) {
84702
+ const tmpPath = `${cachePackageJsonPath}.${crypto2.randomUUID()}`;
84703
+ try {
84704
+ fs12.mkdirSync(path9.dirname(cachePackageJsonPath), { recursive: true });
84705
+ fs12.writeFileSync(tmpPath, JSON.stringify(pkgJson, null, 2));
84706
+ fs12.renameSync(tmpPath, cachePackageJsonPath);
84707
+ return { synced: true, error: null };
84708
+ } catch (err) {
84709
+ log("[auto-update-checker] Failed to write cache package.json:", err);
84710
+ safeUnlink(tmpPath);
84711
+ return { synced: false, error: "write_error", message: "Failed to write cache package.json" };
84712
+ }
84713
+ }
84645
84714
  function syncCachePackageJsonToIntent(pluginInfo) {
84646
84715
  const cachePackageJsonPath = path9.join(CACHE_DIR, "package.json");
84716
+ const intentVersion = getIntentVersion(pluginInfo);
84647
84717
  if (!fs12.existsSync(cachePackageJsonPath)) {
84648
- log("[auto-update-checker] Cache package.json not found, nothing to sync");
84649
- return { synced: false, error: "file_not_found", message: "Cache package.json not found" };
84718
+ log("[auto-update-checker] Cache package.json missing, creating workspace package.json", { intentVersion });
84719
+ return {
84720
+ ...writeCachePackageJson(cachePackageJsonPath, { dependencies: { [PACKAGE_NAME]: intentVersion } }),
84721
+ message: `Created cache package.json with: ${intentVersion}`
84722
+ };
84650
84723
  }
84651
84724
  let content;
84652
84725
  let pkgJson;
@@ -84663,11 +84736,20 @@ function syncCachePackageJsonToIntent(pluginInfo) {
84663
84736
  return { synced: false, error: "parse_error", message: "Failed to parse cache package.json (malformed JSON)" };
84664
84737
  }
84665
84738
  if (!pkgJson || !pkgJson.dependencies?.[PACKAGE_NAME]) {
84666
- log("[auto-update-checker] Plugin not in cache package.json dependencies, nothing to sync");
84667
- return { synced: false, error: "plugin_not_in_deps", message: "Plugin not in cache package.json dependencies" };
84739
+ log("[auto-update-checker] Plugin missing from cache package.json dependencies, adding dependency", { intentVersion });
84740
+ const nextPkgJson = {
84741
+ ...pkgJson ?? {},
84742
+ dependencies: {
84743
+ ...pkgJson?.dependencies ?? {},
84744
+ [PACKAGE_NAME]: intentVersion
84745
+ }
84746
+ };
84747
+ return {
84748
+ ...writeCachePackageJson(cachePackageJsonPath, nextPkgJson),
84749
+ message: `Added ${PACKAGE_NAME}: ${intentVersion}`
84750
+ };
84668
84751
  }
84669
84752
  const currentVersion = pkgJson.dependencies[PACKAGE_NAME];
84670
- const intentVersion = getIntentVersion(pluginInfo);
84671
84753
  if (currentVersion === intentVersion) {
84672
84754
  log("[auto-update-checker] Cache package.json already matches intent:", intentVersion);
84673
84755
  return { synced: false, error: null, message: `Already matches intent: ${intentVersion}` };
@@ -84680,20 +84762,14 @@ function syncCachePackageJsonToIntent(pluginInfo) {
84680
84762
  log(`[auto-update-checker] Updating cache package.json: "${currentVersion}" \u2192 "${intentVersion}"`);
84681
84763
  }
84682
84764
  pkgJson.dependencies[PACKAGE_NAME] = intentVersion;
84683
- const tmpPath = `${cachePackageJsonPath}.${crypto2.randomUUID()}`;
84684
- try {
84685
- fs12.writeFileSync(tmpPath, JSON.stringify(pkgJson, null, 2));
84686
- fs12.renameSync(tmpPath, cachePackageJsonPath);
84687
- return { synced: true, error: null, message: `Updated: "${currentVersion}" \u2192 "${intentVersion}"` };
84688
- } catch (err) {
84689
- log("[auto-update-checker] Failed to write cache package.json:", err);
84690
- safeUnlink(tmpPath);
84691
- return { synced: false, error: "write_error", message: "Failed to write cache package.json" };
84692
- }
84765
+ return {
84766
+ ...writeCachePackageJson(cachePackageJsonPath, pkgJson),
84767
+ message: `Updated: "${currentVersion}" \u2192 "${intentVersion}"`
84768
+ };
84693
84769
  }
84694
84770
  // src/hooks/auto-update-checker/hook/background-update-check.ts
84695
84771
  import { existsSync as existsSync52 } from "fs";
84696
- import { join as join55 } from "path";
84772
+ import { join as join56 } from "path";
84697
84773
  // src/shared/spawn-with-windows-hide.ts
84698
84774
  var {spawn: bunSpawn } = globalThis.Bun;
84699
84775
  import { spawn as nodeSpawn } from "child_process";
@@ -84754,9 +84830,13 @@ function spawnWithWindowsHide(command, options) {
84754
84830
  }
84755
84831
  // src/cli/config-manager/bun-install.ts
84756
84832
  import { existsSync as existsSync50 } from "fs";
84833
+ import { join as join54 } from "path";
84757
84834
  init_logger();
84758
84835
  var BUN_INSTALL_TIMEOUT_SECONDS = 60;
84759
84836
  var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
84837
+ function getDefaultWorkspaceDir() {
84838
+ return join54(getOpenCodeCacheDir(), "packages");
84839
+ }
84760
84840
  function readProcessOutput(stream) {
84761
84841
  if (!stream) {
84762
84842
  return Promise.resolve("");
@@ -84779,7 +84859,7 @@ function logCapturedOutputOnFailure(outputMode, output) {
84779
84859
  }
84780
84860
  async function runBunInstallWithDetails(options) {
84781
84861
  const outputMode = options?.outputMode ?? "pipe";
84782
- const cacheDir = options?.workspaceDir ?? getOpenCodeCacheDir();
84862
+ const cacheDir = options?.workspaceDir ?? getDefaultWorkspaceDir();
84783
84863
  const packageJsonPath = `${cacheDir}/package.json`;
84784
84864
  if (!existsSync50(packageJsonPath)) {
84785
84865
  return {
@@ -84886,8 +84966,9 @@ function removeFromBunLock(packageName) {
84886
84966
  }
84887
84967
  function invalidatePackage(packageName = PACKAGE_NAME) {
84888
84968
  try {
84969
+ const userConfigDir = getUserConfigDir();
84889
84970
  const pkgDirs = [
84890
- path10.join(USER_CONFIG_DIR, "node_modules", packageName),
84971
+ path10.join(userConfigDir, "node_modules", packageName),
84891
84972
  path10.join(CACHE_DIR, "node_modules", packageName)
84892
84973
  ];
84893
84974
  let packageRemoved = false;
@@ -84938,9 +85019,12 @@ Restart OpenCode to apply.`,
84938
85019
  }
84939
85020
 
84940
85021
  // src/hooks/auto-update-checker/hook/background-update-check.ts
85022
+ function getCacheWorkspaceDir(deps) {
85023
+ return deps.join(deps.getOpenCodeCacheDir(), "packages");
85024
+ }
84941
85025
  var defaultDeps = {
84942
85026
  existsSync: existsSync52,
84943
- join: join55,
85027
+ join: join56,
84944
85028
  runBunInstallWithDetails,
84945
85029
  log,
84946
85030
  getOpenCodeCacheDir,
@@ -84959,7 +85043,7 @@ function getPinnedVersionToastMessage(latestVersion) {
84959
85043
  }
84960
85044
  function resolveActiveInstallWorkspace(deps) {
84961
85045
  const configPaths = deps.getOpenCodeConfigPaths({ binary: "opencode" });
84962
- const cacheDir = deps.getOpenCodeCacheDir();
85046
+ const cacheDir = getCacheWorkspaceDir(deps);
84963
85047
  const configInstallPath = deps.join(configPaths.configDir, "node_modules", PACKAGE_NAME, "package.json");
84964
85048
  const cacheInstallPath = deps.join(cacheDir, "node_modules", PACKAGE_NAME, "package.json");
84965
85049
  if (deps.existsSync(configInstallPath)) {
@@ -84970,6 +85054,11 @@ function resolveActiveInstallWorkspace(deps) {
84970
85054
  deps.log(`[auto-update-checker] Active workspace: cache-dir (${cacheDir})`);
84971
85055
  return cacheDir;
84972
85056
  }
85057
+ const cachePackageJsonPath = deps.join(cacheDir, "package.json");
85058
+ if (deps.existsSync(cachePackageJsonPath)) {
85059
+ deps.log(`[auto-update-checker] Active workspace: cache-dir (${cacheDir}, package.json present)`);
85060
+ return cacheDir;
85061
+ }
84973
85062
  deps.log(`[auto-update-checker] Active workspace: config-dir (default, no install detected)`);
84974
85063
  return configPaths.configDir;
84975
85064
  }
@@ -84986,6 +85075,14 @@ async function runBunInstallSafe(workspaceDir, deps) {
84986
85075
  return false;
84987
85076
  }
84988
85077
  }
85078
+ async function primeCacheWorkspace(activeWorkspace, deps) {
85079
+ const cacheWorkspace = getCacheWorkspaceDir(deps);
85080
+ if (activeWorkspace === cacheWorkspace) {
85081
+ return true;
85082
+ }
85083
+ deps.log(`[auto-update-checker] Priming cache workspace after install: ${cacheWorkspace}`);
85084
+ return runBunInstallSafe(cacheWorkspace, deps);
85085
+ }
84989
85086
  function createBackgroundUpdateCheckRunner(overrides = {}) {
84990
85087
  const deps = { ...defaultDeps, ...overrides };
84991
85088
  return async function runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage) {
@@ -85031,6 +85128,12 @@ function createBackgroundUpdateCheckRunner(overrides = {}) {
85031
85128
  const activeWorkspace = resolveActiveInstallWorkspace(deps);
85032
85129
  const installSuccess = await runBunInstallSafe(activeWorkspace, deps);
85033
85130
  if (installSuccess) {
85131
+ const cachePrimed = await primeCacheWorkspace(activeWorkspace, deps);
85132
+ if (!cachePrimed) {
85133
+ await deps.showUpdateAvailableToast(ctx, latestVersion, getToastMessage);
85134
+ deps.log("[auto-update-checker] cache workspace priming failed after install");
85135
+ return;
85136
+ }
85034
85137
  await deps.showAutoUpdatedToast(ctx, currentVersion, latestVersion);
85035
85138
  deps.log(`[auto-update-checker] Update installed: ${currentVersion} \u2192 ${latestVersion}`);
85036
85139
  return;
@@ -85243,16 +85346,16 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
85243
85346
  // src/hooks/agent-usage-reminder/storage.ts
85244
85347
  import {
85245
85348
  existsSync as existsSync53,
85246
- mkdirSync as mkdirSync11,
85349
+ mkdirSync as mkdirSync12,
85247
85350
  readFileSync as readFileSync35,
85248
85351
  writeFileSync as writeFileSync15,
85249
85352
  unlinkSync as unlinkSync8
85250
85353
  } from "fs";
85251
- import { join as join57 } from "path";
85354
+ import { join as join58 } from "path";
85252
85355
 
85253
85356
  // src/hooks/agent-usage-reminder/constants.ts
85254
- import { join as join56 } from "path";
85255
- var AGENT_USAGE_REMINDER_STORAGE = join56(OPENCODE_STORAGE, "agent-usage-reminder");
85357
+ import { join as join57 } from "path";
85358
+ var AGENT_USAGE_REMINDER_STORAGE = join57(OPENCODE_STORAGE, "agent-usage-reminder");
85256
85359
  var TARGET_TOOLS = new Set([
85257
85360
  "grep",
85258
85361
  "safe_grep",
@@ -85298,7 +85401,7 @@ ALWAYS prefer: Multiple parallel task calls > Direct tool calls
85298
85401
 
85299
85402
  // src/hooks/agent-usage-reminder/storage.ts
85300
85403
  function getStoragePath2(sessionID) {
85301
- return join57(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
85404
+ return join58(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
85302
85405
  }
85303
85406
  function loadAgentUsageState(sessionID) {
85304
85407
  const filePath = getStoragePath2(sessionID);
@@ -85313,7 +85416,7 @@ function loadAgentUsageState(sessionID) {
85313
85416
  }
85314
85417
  function saveAgentUsageState(state3) {
85315
85418
  if (!existsSync53(AGENT_USAGE_REMINDER_STORAGE)) {
85316
- mkdirSync11(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
85419
+ mkdirSync12(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
85317
85420
  }
85318
85421
  const filePath = getStoragePath2(state3.sessionID);
85319
85422
  writeFileSync15(filePath, JSON.stringify(state3, null, 2));
@@ -86596,16 +86699,16 @@ function createNonInteractiveEnvHook(_ctx) {
86596
86699
  // src/hooks/interactive-bash-session/storage.ts
86597
86700
  import {
86598
86701
  existsSync as existsSync54,
86599
- mkdirSync as mkdirSync12,
86702
+ mkdirSync as mkdirSync13,
86600
86703
  readFileSync as readFileSync36,
86601
86704
  writeFileSync as writeFileSync16,
86602
86705
  unlinkSync as unlinkSync9
86603
86706
  } from "fs";
86604
- import { join as join59 } from "path";
86707
+ import { join as join60 } from "path";
86605
86708
 
86606
86709
  // src/hooks/interactive-bash-session/constants.ts
86607
- import { join as join58 } from "path";
86608
- var INTERACTIVE_BASH_SESSION_STORAGE = join58(OPENCODE_STORAGE, "interactive-bash-session");
86710
+ import { join as join59 } from "path";
86711
+ var INTERACTIVE_BASH_SESSION_STORAGE = join59(OPENCODE_STORAGE, "interactive-bash-session");
86609
86712
  var OMO_SESSION_PREFIX = "omo-";
86610
86713
  function buildSessionReminderMessage(sessions) {
86611
86714
  if (sessions.length === 0)
@@ -86617,7 +86720,7 @@ function buildSessionReminderMessage(sessions) {
86617
86720
 
86618
86721
  // src/hooks/interactive-bash-session/storage.ts
86619
86722
  function getStoragePath3(sessionID) {
86620
- return join59(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
86723
+ return join60(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
86621
86724
  }
86622
86725
  function loadInteractiveBashSessionState(sessionID) {
86623
86726
  const filePath = getStoragePath3(sessionID);
@@ -86637,7 +86740,7 @@ function loadInteractiveBashSessionState(sessionID) {
86637
86740
  }
86638
86741
  function saveInteractiveBashSessionState(state3) {
86639
86742
  if (!existsSync54(INTERACTIVE_BASH_SESSION_STORAGE)) {
86640
- mkdirSync12(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
86743
+ mkdirSync13(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
86641
86744
  }
86642
86745
  const filePath = getStoragePath3(state3.sessionID);
86643
86746
  const serialized = {
@@ -87155,10 +87258,10 @@ var DEFAULT_MAX_ITERATIONS = 100;
87155
87258
  var DEFAULT_COMPLETION_PROMISE = "DONE";
87156
87259
  var ULTRAWORK_VERIFICATION_PROMISE = "VERIFIED";
87157
87260
  // src/hooks/ralph-loop/storage.ts
87158
- import { existsSync as existsSync55, readFileSync as readFileSync37, writeFileSync as writeFileSync17, unlinkSync as unlinkSync10, mkdirSync as mkdirSync13 } from "fs";
87159
- import { dirname as dirname14, join as join60 } from "path";
87261
+ import { existsSync as existsSync55, readFileSync as readFileSync37, writeFileSync as writeFileSync17, unlinkSync as unlinkSync10, mkdirSync as mkdirSync14 } from "fs";
87262
+ import { dirname as dirname15, join as join61 } from "path";
87160
87263
  function getStateFilePath(directory, customPath) {
87161
- return customPath ? join60(directory, customPath) : join60(directory, DEFAULT_STATE_FILE);
87264
+ return customPath ? join61(directory, customPath) : join61(directory, DEFAULT_STATE_FILE);
87162
87265
  }
87163
87266
  function readState(directory, customPath) {
87164
87267
  const filePath = getStateFilePath(directory, customPath);
@@ -87207,9 +87310,9 @@ function readState(directory, customPath) {
87207
87310
  function writeState(directory, state3, customPath) {
87208
87311
  const filePath = getStateFilePath(directory, customPath);
87209
87312
  try {
87210
- const dir = dirname14(filePath);
87313
+ const dir = dirname15(filePath);
87211
87314
  if (!existsSync55(dir)) {
87212
- mkdirSync13(dir, { recursive: true });
87315
+ mkdirSync14(dir, { recursive: true });
87213
87316
  }
87214
87317
  const sessionIdLine = state3.session_id ? `session_id: "${state3.session_id}"
87215
87318
  ` : "";
@@ -88336,9 +88439,9 @@ function findSlashCommandPartIndex(parts) {
88336
88439
  return -1;
88337
88440
  }
88338
88441
  // src/hooks/auto-slash-command/executor.ts
88339
- import { dirname as dirname17 } from "path";
88442
+ import { dirname as dirname18 } from "path";
88340
88443
  // src/features/opencode-skill-loader/loader.ts
88341
- import { join as join63 } from "path";
88444
+ import { join as join64 } from "path";
88342
88445
  import { homedir as homedir11 } from "os";
88343
88446
 
88344
88447
  // src/features/opencode-skill-loader/skill-definition-record.ts
@@ -88366,7 +88469,7 @@ function deduplicateSkillsByName(skills) {
88366
88469
 
88367
88470
  // src/features/opencode-skill-loader/skill-directory-loader.ts
88368
88471
  import { promises as fs16 } from "fs";
88369
- import { join as join62 } from "path";
88472
+ import { join as join63 } from "path";
88370
88473
 
88371
88474
  // src/features/opencode-skill-loader/loaded-skill-from-path.ts
88372
88475
  import { promises as fs15 } from "fs";
@@ -88384,7 +88487,7 @@ function parseAllowedTools(allowedTools) {
88384
88487
 
88385
88488
  // src/features/opencode-skill-loader/skill-mcp-config.ts
88386
88489
  import { promises as fs14 } from "fs";
88387
- import { join as join61 } from "path";
88490
+ import { join as join62 } from "path";
88388
88491
  function parseSkillMcpConfigFromFrontmatter(content) {
88389
88492
  const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
88390
88493
  if (!frontmatterMatch)
@@ -88400,7 +88503,7 @@ function parseSkillMcpConfigFromFrontmatter(content) {
88400
88503
  return;
88401
88504
  }
88402
88505
  async function loadMcpJsonFromDir(skillDir) {
88403
- const mcpJsonPath = join61(skillDir, "mcp.json");
88506
+ const mcpJsonPath = join62(skillDir, "mcp.json");
88404
88507
  try {
88405
88508
  const content = await fs14.readFile(mcpJsonPath, "utf-8");
88406
88509
  const parsed = JSON.parse(content);
@@ -88489,10 +88592,10 @@ async function loadSkillsFromDir(options) {
88489
88592
  const directories = entries.filter((entry) => !entry.name.startsWith(".") && (entry.isDirectory() || entry.isSymbolicLink()));
88490
88593
  const files = entries.filter((entry) => !entry.name.startsWith(".") && !entry.isDirectory() && !entry.isSymbolicLink() && isMarkdownFile(entry));
88491
88594
  for (const entry of directories) {
88492
- const entryPath = join62(options.skillsDir, entry.name);
88595
+ const entryPath = join63(options.skillsDir, entry.name);
88493
88596
  const resolvedPath = await resolveSymlinkAsync(entryPath);
88494
88597
  const dirName = entry.name;
88495
- const skillMdPath = join62(resolvedPath, "SKILL.md");
88598
+ const skillMdPath = join63(resolvedPath, "SKILL.md");
88496
88599
  try {
88497
88600
  await fs16.access(skillMdPath);
88498
88601
  const skill = await loadSkillFromPath({
@@ -88507,7 +88610,7 @@ async function loadSkillsFromDir(options) {
88507
88610
  }
88508
88611
  continue;
88509
88612
  } catch {}
88510
- const namedSkillMdPath = join62(resolvedPath, `${dirName}.md`);
88613
+ const namedSkillMdPath = join63(resolvedPath, `${dirName}.md`);
88511
88614
  try {
88512
88615
  await fs16.access(namedSkillMdPath);
88513
88616
  const skill = await loadSkillFromPath({
@@ -88539,7 +88642,7 @@ async function loadSkillsFromDir(options) {
88539
88642
  }
88540
88643
  }
88541
88644
  for (const entry of files) {
88542
- const entryPath = join62(options.skillsDir, entry.name);
88645
+ const entryPath = join63(options.skillsDir, entry.name);
88543
88646
  const baseName = inferSkillNameFromFileName(entryPath);
88544
88647
  const skill = await loadSkillFromPath({
88545
88648
  skillPath: entryPath,
@@ -88557,7 +88660,7 @@ async function loadSkillsFromDir(options) {
88557
88660
 
88558
88661
  // src/features/opencode-skill-loader/loader.ts
88559
88662
  async function loadUserSkills() {
88560
- const userSkillsDir = join63(getClaudeConfigDir(), "skills");
88663
+ const userSkillsDir = join64(getClaudeConfigDir(), "skills");
88561
88664
  const skills = await loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
88562
88665
  return skillsToCommandDefinitionRecord(skills);
88563
88666
  }
@@ -88582,7 +88685,7 @@ async function loadProjectAgentsSkills(directory) {
88582
88685
  return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat()));
88583
88686
  }
88584
88687
  async function loadGlobalAgentsSkills() {
88585
- const agentsGlobalDir = join63(homedir11(), ".agents", "skills");
88688
+ const agentsGlobalDir = join64(homedir11(), ".agents", "skills");
88586
88689
  const skills = await loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
88587
88690
  return skillsToCommandDefinitionRecord(skills);
88588
88691
  }
@@ -88629,7 +88732,7 @@ async function discoverSkills(options = {}) {
88629
88732
  ]);
88630
88733
  }
88631
88734
  async function discoverUserClaudeSkills() {
88632
- const userSkillsDir = join63(getClaudeConfigDir(), "skills");
88735
+ const userSkillsDir = join64(getClaudeConfigDir(), "skills");
88633
88736
  return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
88634
88737
  }
88635
88738
  async function discoverProjectClaudeSkills(directory) {
@@ -88653,7 +88756,7 @@ async function discoverProjectAgentsSkills(directory) {
88653
88756
  return deduplicateSkillsByName(allSkills.flat());
88654
88757
  }
88655
88758
  async function discoverGlobalAgentsSkills() {
88656
- const agentsGlobalDir = join63(homedir11(), ".agents", "skills");
88759
+ const agentsGlobalDir = join64(homedir11(), ".agents", "skills");
88657
88760
  return loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
88658
88761
  }
88659
88762
  // src/features/opencode-skill-loader/merger/builtin-skill-converter.ts
@@ -88681,7 +88784,7 @@ function builtinToLoadedSkill(builtin) {
88681
88784
 
88682
88785
  // src/features/opencode-skill-loader/merger/config-skill-entry-loader.ts
88683
88786
  import { existsSync as existsSync57, readFileSync as readFileSync39 } from "fs";
88684
- import { dirname as dirname15, isAbsolute as isAbsolute7, resolve as resolve10 } from "path";
88787
+ import { dirname as dirname16, isAbsolute as isAbsolute7, resolve as resolve10 } from "path";
88685
88788
  import { homedir as homedir12 } from "os";
88686
88789
  init_logger();
88687
88790
  function resolveFilePath5(from, configDir) {
@@ -88736,7 +88839,7 @@ function configEntryToLoadedSkill(name, entry, configDir) {
88736
88839
  return null;
88737
88840
  }
88738
88841
  const description = entry.description || fileMetadata.description || "";
88739
- const resolvedPath = sourcePath ? dirname15(sourcePath) : configDir || process.cwd();
88842
+ const resolvedPath = sourcePath ? dirname16(sourcePath) : configDir || process.cwd();
88740
88843
  const resolvedTemplate = resolveSkillPathReferences(template.trim(), resolvedPath);
88741
88844
  const wrappedTemplate = `<skill-instruction>
88742
88845
  Base directory for this skill: ${resolvedPath}/
@@ -92467,7 +92570,7 @@ async function resolveMultipleSkillsAsync(skillNames, options) {
92467
92570
  var import_picomatch2 = __toESM(require_picomatch2(), 1);
92468
92571
  import { promises as fs17 } from "fs";
92469
92572
  import { homedir as homedir13 } from "os";
92470
- import { dirname as dirname16, extname, isAbsolute as isAbsolute8, join as join64, relative as relative6 } from "path";
92573
+ import { dirname as dirname17, extname, isAbsolute as isAbsolute8, join as join65, relative as relative6 } from "path";
92471
92574
  var MAX_RECURSIVE_DEPTH = 10;
92472
92575
  function isHttpUrl(path11) {
92473
92576
  return path11.startsWith("http://") || path11.startsWith("https://");
@@ -92477,12 +92580,12 @@ function toAbsolutePath(path11, configDir) {
92477
92580
  return homedir13();
92478
92581
  }
92479
92582
  if (path11.startsWith("~/")) {
92480
- return join64(homedir13(), path11.slice(2));
92583
+ return join65(homedir13(), path11.slice(2));
92481
92584
  }
92482
92585
  if (isAbsolute8(path11)) {
92483
92586
  return path11;
92484
92587
  }
92485
- return join64(configDir, path11);
92588
+ return join65(configDir, path11);
92486
92589
  }
92487
92590
  function isMarkdownPath(path11) {
92488
92591
  return extname(path11).toLowerCase() === ".md";
@@ -92513,13 +92616,13 @@ async function loadSourcePath(options) {
92513
92616
  return [];
92514
92617
  const loaded = await loadSkillFromPath({
92515
92618
  skillPath: absolutePath,
92516
- resolvedPath: dirname16(absolutePath),
92619
+ resolvedPath: dirname17(absolutePath),
92517
92620
  defaultName: inferSkillNameFromFileName(absolutePath),
92518
92621
  scope: "config"
92519
92622
  });
92520
92623
  if (!loaded)
92521
92624
  return [];
92522
- return filterByGlob([loaded], dirname16(absolutePath), options.globPattern);
92625
+ return filterByGlob([loaded], dirname17(absolutePath), options.globPattern);
92523
92626
  }
92524
92627
  if (!stat.isDirectory())
92525
92628
  return [];
@@ -92553,7 +92656,7 @@ async function discoverConfigSourceSkills(options) {
92553
92656
  }
92554
92657
  // src/tools/slashcommand/command-discovery.ts
92555
92658
  import { existsSync as existsSync58, readdirSync as readdirSync15, readFileSync as readFileSync41, statSync as statSync6 } from "fs";
92556
- import { basename as basename7, join as join65 } from "path";
92659
+ import { basename as basename7, join as join66 } from "path";
92557
92660
  // src/features/builtin-commands/templates/init-deep.ts
92558
92661
  var INIT_DEEP_TEMPLATE = `# /init-deep
92559
92662
 
@@ -94104,12 +94207,12 @@ function discoverCommandsFromDir(commandsDir, scope, prefix = "") {
94104
94207
  if (entry.name.startsWith("."))
94105
94208
  continue;
94106
94209
  const nestedPrefix = prefix ? `${prefix}${NESTED_COMMAND_SEPARATOR}${entry.name}` : entry.name;
94107
- commands3.push(...discoverCommandsFromDir(join65(commandsDir, entry.name), scope, nestedPrefix));
94210
+ commands3.push(...discoverCommandsFromDir(join66(commandsDir, entry.name), scope, nestedPrefix));
94108
94211
  continue;
94109
94212
  }
94110
94213
  if (!isMarkdownFile(entry))
94111
94214
  continue;
94112
- const commandPath = join65(commandsDir, entry.name);
94215
+ const commandPath = join66(commandsDir, entry.name);
94113
94216
  const baseCommandName = basename7(entry.name, ".md");
94114
94217
  const commandName = prefix ? `${prefix}${NESTED_COMMAND_SEPARATOR}${baseCommandName}` : baseCommandName;
94115
94218
  try {
@@ -94165,8 +94268,8 @@ function deduplicateCommandInfosByName(commands3) {
94165
94268
  return deduplicatedCommands;
94166
94269
  }
94167
94270
  function discoverCommandsSync(directory, options) {
94168
- const userCommandsDir = join65(getClaudeConfigDir(), "commands");
94169
- const projectCommandsDir = join65(directory ?? process.cwd(), ".claude", "commands");
94271
+ const userCommandsDir = join66(getClaudeConfigDir(), "commands");
94272
+ const projectCommandsDir = join66(directory ?? process.cwd(), ".claude", "commands");
94170
94273
  const opencodeGlobalDirs = getOpenCodeCommandDirs({ binary: "opencode" });
94171
94274
  const opencodeProjectDirs = findProjectOpencodeCommandDirs(directory ?? process.cwd());
94172
94275
  const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
@@ -94269,7 +94372,7 @@ async function formatCommandTemplate(cmd, args) {
94269
94372
  if (!content && cmd.lazyContentLoader) {
94270
94373
  content = await cmd.lazyContentLoader.load();
94271
94374
  }
94272
- const commandDir = cmd.path ? dirname17(cmd.path) : process.cwd();
94375
+ const commandDir = cmd.path ? dirname18(cmd.path) : process.cwd();
94273
94376
  const withFileRefs = await resolveFileReferencesInText(content, commandDir);
94274
94377
  const resolvedContent = await resolveCommandsInText(withFileRefs);
94275
94378
  const resolvedArguments = args;
@@ -94651,11 +94754,11 @@ var NOTEPAD_DIR = "notepads";
94651
94754
  var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
94652
94755
  var PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
94653
94756
  // src/features/boulder-state/storage.ts
94654
- import { existsSync as existsSync59, readFileSync as readFileSync42, writeFileSync as writeFileSync18, mkdirSync as mkdirSync14, readdirSync as readdirSync16 } from "fs";
94655
- import { dirname as dirname18, join as join66, basename as basename8 } from "path";
94757
+ import { existsSync as existsSync59, readFileSync as readFileSync42, writeFileSync as writeFileSync18, mkdirSync as mkdirSync15, readdirSync as readdirSync16 } from "fs";
94758
+ import { dirname as dirname19, join as join67, basename as basename8 } from "path";
94656
94759
  var RESERVED_KEYS = new Set(["__proto__", "prototype", "constructor"]);
94657
94760
  function getBoulderFilePath(directory) {
94658
- return join66(directory, BOULDER_DIR, BOULDER_FILE);
94761
+ return join67(directory, BOULDER_DIR, BOULDER_FILE);
94659
94762
  }
94660
94763
  function readBoulderState(directory) {
94661
94764
  const filePath = getBoulderFilePath(directory);
@@ -94682,9 +94785,9 @@ function readBoulderState(directory) {
94682
94785
  function writeBoulderState(directory, state3) {
94683
94786
  const filePath = getBoulderFilePath(directory);
94684
94787
  try {
94685
- const dir = dirname18(filePath);
94788
+ const dir = dirname19(filePath);
94686
94789
  if (!existsSync59(dir)) {
94687
- mkdirSync14(dir, { recursive: true });
94790
+ mkdirSync15(dir, { recursive: true });
94688
94791
  }
94689
94792
  writeFileSync18(filePath, JSON.stringify(state3, null, 2), "utf-8");
94690
94793
  return true;
@@ -94754,13 +94857,13 @@ function upsertTaskSessionState(directory, input) {
94754
94857
  return null;
94755
94858
  }
94756
94859
  function findPrometheusPlans(directory) {
94757
- const plansDir = join66(directory, PROMETHEUS_PLANS_DIR);
94860
+ const plansDir = join67(directory, PROMETHEUS_PLANS_DIR);
94758
94861
  if (!existsSync59(plansDir)) {
94759
94862
  return [];
94760
94863
  }
94761
94864
  try {
94762
94865
  const files = readdirSync16(plansDir);
94763
- return files.filter((f) => f.endsWith(".md")).map((f) => join66(plansDir, f)).sort((a, b) => {
94866
+ return files.filter((f) => f.endsWith(".md")).map((f) => join67(plansDir, f)).sort((a, b) => {
94764
94867
  const aStat = __require("fs").statSync(a);
94765
94868
  const bStat = __require("fs").statSync(b);
94766
94869
  return bStat.mtimeMs - aStat.mtimeMs;
@@ -98049,11 +98152,11 @@ import * as path11 from "path";
98049
98152
  // src/shared/migrate-legacy-config-file.ts
98050
98153
  init_logger();
98051
98154
  import { existsSync as existsSync62, readFileSync as readFileSync45, renameSync as renameSync4, rmSync as rmSync3 } from "fs";
98052
- import { join as join67, dirname as dirname19, basename as basename9 } from "path";
98155
+ import { join as join68, dirname as dirname20, basename as basename9 } from "path";
98053
98156
  function buildCanonicalPath(legacyPath) {
98054
- const dir = dirname19(legacyPath);
98157
+ const dir = dirname20(legacyPath);
98055
98158
  const ext = basename9(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
98056
- return join67(dir, `${CONFIG_BASENAME}${ext}`);
98159
+ return join68(dir, `${CONFIG_BASENAME}${ext}`);
98057
98160
  }
98058
98161
  function archiveLegacyConfigFile(legacyPath) {
98059
98162
  const backupPath = `${legacyPath}.bak`;
@@ -98236,7 +98339,7 @@ function mergeConfigs(base, override) {
98236
98339
  function loadPluginConfig(directory, ctx) {
98237
98340
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
98238
98341
  const userDetected = detectPluginConfigFile(configDir);
98239
- let userConfigPath = userDetected.format !== "none" ? userDetected.path : path11.join(configDir, "oh-my-opencode.json");
98342
+ let userConfigPath = userDetected.format !== "none" ? userDetected.path : path11.join(configDir, `${CONFIG_BASENAME}.json`);
98240
98343
  if (userDetected.legacyPath) {
98241
98344
  log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
98242
98345
  canonicalPath: userDetected.path,
@@ -98244,12 +98347,15 @@ function loadPluginConfig(directory, ctx) {
98244
98347
  });
98245
98348
  }
98246
98349
  if (userDetected.format !== "none" && path11.basename(userDetected.path).startsWith(LEGACY_CONFIG_BASENAME)) {
98247
- migrateLegacyConfigFile(userDetected.path);
98248
- userConfigPath = path11.join(path11.dirname(userDetected.path), `${CONFIG_BASENAME}${path11.extname(userDetected.path)}`);
98350
+ const migrated = migrateLegacyConfigFile(userDetected.path);
98351
+ const canonicalPath = path11.join(path11.dirname(userDetected.path), `${CONFIG_BASENAME}${path11.extname(userDetected.path)}`);
98352
+ if (migrated || fs18.existsSync(canonicalPath)) {
98353
+ userConfigPath = canonicalPath;
98354
+ }
98249
98355
  }
98250
98356
  const projectBasePath = path11.join(directory, ".opencode");
98251
98357
  const projectDetected = detectPluginConfigFile(projectBasePath);
98252
- let projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : path11.join(projectBasePath, "oh-my-opencode.json");
98358
+ let projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : path11.join(projectBasePath, `${CONFIG_BASENAME}.json`);
98253
98359
  if (projectDetected.legacyPath) {
98254
98360
  log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
98255
98361
  canonicalPath: projectDetected.path,
@@ -98257,8 +98363,11 @@ function loadPluginConfig(directory, ctx) {
98257
98363
  });
98258
98364
  }
98259
98365
  if (projectDetected.format !== "none" && path11.basename(projectDetected.path).startsWith(LEGACY_CONFIG_BASENAME)) {
98260
- migrateLegacyConfigFile(projectDetected.path);
98261
- projectConfigPath = path11.join(path11.dirname(projectDetected.path), `${CONFIG_BASENAME}${path11.extname(projectDetected.path)}`);
98366
+ const projectMigrated = migrateLegacyConfigFile(projectDetected.path);
98367
+ const canonicalProjectPath = path11.join(path11.dirname(projectDetected.path), `${CONFIG_BASENAME}${path11.extname(projectDetected.path)}`);
98368
+ if (projectMigrated || fs18.existsSync(canonicalProjectPath)) {
98369
+ projectConfigPath = canonicalProjectPath;
98370
+ }
98262
98371
  }
98263
98372
  const userConfig = loadConfigFromPath2(userConfigPath, ctx);
98264
98373
  let config2 = userConfig ?? OhMyOpenCodeConfigSchema.parse({});
@@ -99570,7 +99679,7 @@ function createRuntimeFallbackHook(ctx, options) {
99570
99679
  }
99571
99680
  // src/hooks/write-existing-file-guard/hook.ts
99572
99681
  import { existsSync as existsSync65, realpathSync as realpathSync7 } from "fs";
99573
- import { basename as basename11, dirname as dirname21, isAbsolute as isAbsolute10, join as join69, normalize as normalize2, relative as relative8, resolve as resolve12 } from "path";
99682
+ import { basename as basename11, dirname as dirname22, isAbsolute as isAbsolute10, join as join70, normalize as normalize2, relative as relative8, resolve as resolve12 } from "path";
99574
99683
 
99575
99684
  // src/hooks/write-existing-file-guard/tool-execute-before-handler.ts
99576
99685
  import { existsSync as existsSync64 } from "fs";
@@ -99743,9 +99852,9 @@ function toCanonicalPath2(absolutePath) {
99743
99852
  canonicalPath = absolutePath;
99744
99853
  }
99745
99854
  } else {
99746
- const absoluteDir = dirname21(absolutePath);
99855
+ const absoluteDir = dirname22(absolutePath);
99747
99856
  const resolvedDir = existsSync65(absoluteDir) ? realpathSync7.native(absoluteDir) : absoluteDir;
99748
- canonicalPath = join69(resolvedDir, basename11(absolutePath));
99857
+ canonicalPath = join70(resolvedDir, basename11(absolutePath));
99749
99858
  }
99750
99859
  return normalize2(canonicalPath);
99751
99860
  }
@@ -100761,11 +100870,11 @@ init_logger();
100761
100870
 
100762
100871
  // src/hooks/legacy-plugin-toast/auto-migrate.ts
100763
100872
  import { existsSync as existsSync66, readFileSync as readFileSync47 } from "fs";
100764
- import { join as join70 } from "path";
100873
+ import { join as join71 } from "path";
100765
100874
  function detectOpenCodeConfigPath(overrideConfigDir) {
100766
100875
  if (overrideConfigDir) {
100767
- const jsoncPath = join70(overrideConfigDir, "opencode.jsonc");
100768
- const jsonPath = join70(overrideConfigDir, "opencode.json");
100876
+ const jsoncPath = join71(overrideConfigDir, "opencode.jsonc");
100877
+ const jsonPath = join71(overrideConfigDir, "opencode.json");
100769
100878
  if (existsSync66(jsoncPath))
100770
100879
  return jsoncPath;
100771
100880
  if (existsSync66(jsonPath))
@@ -101179,7 +101288,7 @@ var DEFAULT_MAX_DIAGNOSTICS = 200;
101179
101288
  var DEFAULT_MAX_DIRECTORY_FILES = 50;
101180
101289
  // src/tools/lsp/server-config-loader.ts
101181
101290
  import { existsSync as existsSync67, readFileSync as readFileSync48 } from "fs";
101182
- import { join as join71 } from "path";
101291
+ import { join as join72 } from "path";
101183
101292
  function loadJsonFile(path12) {
101184
101293
  if (!existsSync67(path12))
101185
101294
  return null;
@@ -101193,9 +101302,9 @@ function getConfigPaths3() {
101193
101302
  const cwd = process.cwd();
101194
101303
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
101195
101304
  return {
101196
- project: detectPluginConfigFile(join71(cwd, ".opencode")).path,
101305
+ project: detectPluginConfigFile(join72(cwd, ".opencode")).path,
101197
101306
  user: detectPluginConfigFile(configDir).path,
101198
- opencode: detectConfigFile(join71(configDir, "opencode")).path
101307
+ opencode: detectConfigFile(join72(configDir, "opencode")).path
101199
101308
  };
101200
101309
  }
101201
101310
  function loadAllConfigs() {
@@ -101265,19 +101374,19 @@ function getMergedServers() {
101265
101374
 
101266
101375
  // src/tools/lsp/server-installation.ts
101267
101376
  import { existsSync as existsSync68 } from "fs";
101268
- import { delimiter, join as join73 } from "path";
101377
+ import { delimiter, join as join74 } from "path";
101269
101378
 
101270
101379
  // src/tools/lsp/server-path-bases.ts
101271
- import { join as join72 } from "path";
101380
+ import { join as join73 } from "path";
101272
101381
  function getLspServerAdditionalPathBases(workingDirectory) {
101273
101382
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
101274
- const dataDir = join72(getDataDir(), "opencode");
101383
+ const dataDir = join73(getDataDir(), "opencode");
101275
101384
  return [
101276
- join72(workingDirectory, "node_modules", ".bin"),
101277
- join72(configDir, "bin"),
101278
- join72(configDir, "node_modules", ".bin"),
101279
- join72(dataDir, "bin"),
101280
- join72(dataDir, "bin", "node_modules", ".bin")
101385
+ join73(workingDirectory, "node_modules", ".bin"),
101386
+ join73(configDir, "bin"),
101387
+ join73(configDir, "node_modules", ".bin"),
101388
+ join73(dataDir, "bin"),
101389
+ join73(dataDir, "bin", "node_modules", ".bin")
101281
101390
  ];
101282
101391
  }
101283
101392
 
@@ -101308,14 +101417,14 @@ function isServerInstalled(command) {
101308
101417
  const paths = pathEnv.split(delimiter);
101309
101418
  for (const p of paths) {
101310
101419
  for (const suffix of exts) {
101311
- if (existsSync68(join73(p, cmd + suffix))) {
101420
+ if (existsSync68(join74(p, cmd + suffix))) {
101312
101421
  return true;
101313
101422
  }
101314
101423
  }
101315
101424
  }
101316
101425
  for (const base of getLspServerAdditionalPathBases(process.cwd())) {
101317
101426
  for (const suffix of exts) {
101318
- if (existsSync68(join73(base, cmd + suffix))) {
101427
+ if (existsSync68(join74(base, cmd + suffix))) {
101319
101428
  return true;
101320
101429
  }
101321
101430
  }
@@ -114888,7 +114997,7 @@ import { resolve as resolve16 } from "path";
114888
114997
 
114889
114998
  // src/tools/lsp/directory-diagnostics.ts
114890
114999
  import { existsSync as existsSync71, lstatSync as lstatSync2, readdirSync as readdirSync17 } from "fs";
114891
- import { extname as extname6, join as join74, resolve as resolve15 } from "path";
115000
+ import { extname as extname6, join as join75, resolve as resolve15 } from "path";
114892
115001
  var SKIP_DIRECTORIES = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
114893
115002
  function collectFilesWithExtension(dir, extension, maxFiles) {
114894
115003
  const files = [];
@@ -114904,7 +115013,7 @@ function collectFilesWithExtension(dir, extension, maxFiles) {
114904
115013
  for (const entry of entries) {
114905
115014
  if (files.length >= maxFiles)
114906
115015
  return;
114907
- const fullPath = join74(currentDir, entry);
115016
+ const fullPath = join75(currentDir, entry);
114908
115017
  let stat;
114909
115018
  try {
114910
115019
  stat = lstatSync2(fullPath);
@@ -115007,7 +115116,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
115007
115116
 
115008
115117
  // src/tools/lsp/infer-extension.ts
115009
115118
  import { readdirSync as readdirSync18, lstatSync as lstatSync3 } from "fs";
115010
- import { extname as extname7, join as join75 } from "path";
115119
+ import { extname as extname7, join as join76 } from "path";
115011
115120
  var SKIP_DIRECTORIES2 = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
115012
115121
  var MAX_SCAN_ENTRIES = 500;
115013
115122
  function inferExtensionFromDirectory(directory) {
@@ -115025,7 +115134,7 @@ function inferExtensionFromDirectory(directory) {
115025
115134
  for (const entry of entries) {
115026
115135
  if (scanned >= MAX_SCAN_ENTRIES)
115027
115136
  return;
115028
- const fullPath = join75(dir, entry);
115137
+ const fullPath = join76(dir, entry);
115029
115138
  let stat;
115030
115139
  try {
115031
115140
  stat = lstatSync3(fullPath);
@@ -115190,12 +115299,12 @@ var DEFAULT_MAX_MATCHES = 500;
115190
115299
 
115191
115300
  // src/tools/ast-grep/sg-cli-path.ts
115192
115301
  import { createRequire as createRequire4 } from "module";
115193
- import { dirname as dirname22, join as join77 } from "path";
115302
+ import { dirname as dirname23, join as join78 } from "path";
115194
115303
  import { existsSync as existsSync73, statSync as statSync10 } from "fs";
115195
115304
 
115196
115305
  // src/tools/ast-grep/downloader.ts
115197
115306
  import { existsSync as existsSync72 } from "fs";
115198
- import { join as join76 } from "path";
115307
+ import { join as join77 } from "path";
115199
115308
  import { homedir as homedir14 } from "os";
115200
115309
  import { createRequire as createRequire3 } from "module";
115201
115310
  init_logger();
@@ -115222,12 +115331,12 @@ var PLATFORM_MAP2 = {
115222
115331
  function getCacheDir3() {
115223
115332
  if (process.platform === "win32") {
115224
115333
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
115225
- const base2 = localAppData || join76(homedir14(), "AppData", "Local");
115226
- return join76(base2, "oh-my-opencode", "bin");
115334
+ const base2 = localAppData || join77(homedir14(), "AppData", "Local");
115335
+ return join77(base2, "oh-my-opencode", "bin");
115227
115336
  }
115228
115337
  const xdgCache = process.env.XDG_CACHE_HOME;
115229
- const base = xdgCache || join76(homedir14(), ".cache");
115230
- return join76(base, "oh-my-opencode", "bin");
115338
+ const base = xdgCache || join77(homedir14(), ".cache");
115339
+ return join77(base, "oh-my-opencode", "bin");
115231
115340
  }
115232
115341
  function getBinaryName3() {
115233
115342
  return process.platform === "win32" ? "sg.exe" : "sg";
@@ -115244,7 +115353,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
115244
115353
  }
115245
115354
  const cacheDir = getCacheDir3();
115246
115355
  const binaryName = getBinaryName3();
115247
- const binaryPath = join76(cacheDir, binaryName);
115356
+ const binaryPath = join77(cacheDir, binaryName);
115248
115357
  if (existsSync72(binaryPath)) {
115249
115358
  return binaryPath;
115250
115359
  }
@@ -115253,7 +115362,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
115253
115362
  const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
115254
115363
  log(`[oh-my-opencode] Downloading ast-grep binary...`);
115255
115364
  try {
115256
- const archivePath = join76(cacheDir, assetName);
115365
+ const archivePath = join77(cacheDir, assetName);
115257
115366
  ensureCacheDir(cacheDir);
115258
115367
  await downloadArchive(downloadUrl, archivePath);
115259
115368
  await extractZipArchive(archivePath, cacheDir);
@@ -115306,8 +115415,8 @@ function findSgCliPathSync() {
115306
115415
  try {
115307
115416
  const require2 = createRequire4(import.meta.url);
115308
115417
  const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
115309
- const cliDirectory = dirname22(cliPackageJsonPath);
115310
- const sgPath = join77(cliDirectory, binaryName);
115418
+ const cliDirectory = dirname23(cliPackageJsonPath);
115419
+ const sgPath = join78(cliDirectory, binaryName);
115311
115420
  if (existsSync73(sgPath) && isValidBinary(sgPath)) {
115312
115421
  return sgPath;
115313
115422
  }
@@ -115317,9 +115426,9 @@ function findSgCliPathSync() {
115317
115426
  try {
115318
115427
  const require2 = createRequire4(import.meta.url);
115319
115428
  const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
115320
- const packageDirectory = dirname22(packageJsonPath);
115429
+ const packageDirectory = dirname23(packageJsonPath);
115321
115430
  const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
115322
- const binaryPath = join77(packageDirectory, astGrepBinaryName);
115431
+ const binaryPath = join78(packageDirectory, astGrepBinaryName);
115323
115432
  if (existsSync73(binaryPath) && isValidBinary(binaryPath)) {
115324
115433
  return binaryPath;
115325
115434
  }
@@ -115721,18 +115830,18 @@ var {spawn: spawn18 } = globalThis.Bun;
115721
115830
 
115722
115831
  // src/tools/grep/constants.ts
115723
115832
  import { existsSync as existsSync77 } from "fs";
115724
- import { join as join79, dirname as dirname23 } from "path";
115833
+ import { join as join80, dirname as dirname24 } from "path";
115725
115834
  import { spawnSync as spawnSync4 } from "child_process";
115726
115835
 
115727
115836
  // src/tools/grep/downloader.ts
115728
115837
  import { existsSync as existsSync76, readdirSync as readdirSync19 } from "fs";
115729
- import { join as join78 } from "path";
115838
+ import { join as join79 } from "path";
115730
115839
  function findFileRecursive(dir, filename) {
115731
115840
  try {
115732
115841
  const entries = readdirSync19(dir, { withFileTypes: true, recursive: true });
115733
115842
  for (const entry of entries) {
115734
115843
  if (entry.isFile() && entry.name === filename) {
115735
- return join78(entry.parentPath ?? dir, entry.name);
115844
+ return join79(entry.parentPath ?? dir, entry.name);
115736
115845
  }
115737
115846
  }
115738
115847
  } catch {
@@ -115753,11 +115862,11 @@ function getPlatformKey() {
115753
115862
  }
115754
115863
  function getInstallDir() {
115755
115864
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
115756
- return join78(homeDir, ".cache", "oh-my-opencode", "bin");
115865
+ return join79(homeDir, ".cache", "oh-my-opencode", "bin");
115757
115866
  }
115758
115867
  function getRgPath() {
115759
115868
  const isWindows2 = process.platform === "win32";
115760
- return join78(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
115869
+ return join79(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
115761
115870
  }
115762
115871
  async function extractTarGz2(archivePath, destDir) {
115763
115872
  const platformKey = getPlatformKey();
@@ -115774,7 +115883,7 @@ async function extractZip2(archivePath, destDir) {
115774
115883
  const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
115775
115884
  const foundPath = findFileRecursive(destDir, binaryName);
115776
115885
  if (foundPath) {
115777
- const destPath = join78(destDir, binaryName);
115886
+ const destPath = join79(destDir, binaryName);
115778
115887
  if (foundPath !== destPath) {
115779
115888
  const { renameSync: renameSync5 } = await import("fs");
115780
115889
  renameSync5(foundPath, destPath);
@@ -115795,7 +115904,7 @@ async function downloadAndInstallRipgrep() {
115795
115904
  ensureCacheDir(installDir);
115796
115905
  const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
115797
115906
  const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
115798
- const archivePath = join78(installDir, filename);
115907
+ const archivePath = join79(installDir, filename);
115799
115908
  try {
115800
115909
  await downloadArchive(url3, archivePath);
115801
115910
  if (config4.extension === "tar.gz") {
@@ -115837,15 +115946,15 @@ function findExecutable(name) {
115837
115946
  }
115838
115947
  function getOpenCodeBundledRg() {
115839
115948
  const execPath = process.execPath;
115840
- const execDir = dirname23(execPath);
115949
+ const execDir = dirname24(execPath);
115841
115950
  const isWindows2 = process.platform === "win32";
115842
115951
  const rgName = isWindows2 ? "rg.exe" : "rg";
115843
115952
  const candidates = [
115844
- join79(getDataDir(), "opencode", "bin", rgName),
115845
- join79(execDir, rgName),
115846
- join79(execDir, "bin", rgName),
115847
- join79(execDir, "..", "bin", rgName),
115848
- join79(execDir, "..", "libexec", rgName)
115953
+ join80(getDataDir(), "opencode", "bin", rgName),
115954
+ join80(execDir, rgName),
115955
+ join80(execDir, "bin", rgName),
115956
+ join80(execDir, "..", "bin", rgName),
115957
+ join80(execDir, "..", "libexec", rgName)
115849
115958
  ];
115850
115959
  for (const candidate of candidates) {
115851
115960
  if (existsSync77(candidate)) {
@@ -116496,9 +116605,9 @@ Use this when a task matches an available skill's or command's description.
116496
116605
  - The tool will return detailed instructions with your context applied.
116497
116606
  `;
116498
116607
  // src/tools/skill/tools.ts
116499
- import { dirname as dirname25 } from "path";
116608
+ import { dirname as dirname26 } from "path";
116500
116609
  // src/tools/slashcommand/command-output-formatter.ts
116501
- import { dirname as dirname24 } from "path";
116610
+ import { dirname as dirname25 } from "path";
116502
116611
  async function formatLoadedCommand(command, userMessage) {
116503
116612
  const sections = [];
116504
116613
  sections.push(`# /${command.name} Command
@@ -116537,7 +116646,7 @@ async function formatLoadedCommand(command, userMessage) {
116537
116646
  if (!content && command.lazyContentLoader) {
116538
116647
  content = await command.lazyContentLoader.load();
116539
116648
  }
116540
- const commandDir = command.path ? dirname24(command.path) : process.cwd();
116649
+ const commandDir = command.path ? dirname25(command.path) : process.cwd();
116541
116650
  const withFileReferences = await resolveFileReferencesInText(content, commandDir);
116542
116651
  const resolvedContent = await resolveCommandsInText(withFileReferences);
116543
116652
  let finalContent = resolvedContent.trim();
@@ -116917,7 +117026,7 @@ function createSkillTool(options = {}) {
116917
117026
  if (matchedSkill.name === "git-master") {
116918
117027
  body = injectGitMasterConfig(body, options.gitMasterConfig);
116919
117028
  }
116920
- const dir = matchedSkill.path ? dirname25(matchedSkill.path) : matchedSkill.resolvedPath || process.cwd();
117029
+ const dir = matchedSkill.path ? dirname26(matchedSkill.path) : matchedSkill.resolvedPath || process.cwd();
116921
117030
  const output = [
116922
117031
  `## Skill: ${matchedSkill.name}`,
116923
117032
  "",
@@ -116957,9 +117066,9 @@ function createSkillTool(options = {}) {
116957
117066
  }
116958
117067
  var skill = createSkillTool();
116959
117068
  // src/tools/session-manager/constants.ts
116960
- import { join as join80 } from "path";
116961
- var TODO_DIR2 = join80(getClaudeConfigDir(), "todos");
116962
- var TRANSCRIPT_DIR2 = join80(getClaudeConfigDir(), "transcripts");
117069
+ import { join as join81 } from "path";
117070
+ var TODO_DIR2 = join81(getClaudeConfigDir(), "todos");
117071
+ var TRANSCRIPT_DIR2 = join81(getClaudeConfigDir(), "transcripts");
116963
117072
  var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
116964
117073
 
116965
117074
  Returns a list of available session IDs with metadata including message count, date range, and agents used.
@@ -117034,7 +117143,7 @@ Has Transcript: Yes (234 entries)`;
117034
117143
  // src/tools/session-manager/file-storage.ts
117035
117144
  import { existsSync as existsSync78 } from "fs";
117036
117145
  import { readdir, readFile } from "fs/promises";
117037
- import { join as join81 } from "path";
117146
+ import { join as join82 } from "path";
117038
117147
  async function getFileMainSessions(directory) {
117039
117148
  if (!existsSync78(SESSION_STORAGE))
117040
117149
  return [];
@@ -117044,13 +117153,13 @@ async function getFileMainSessions(directory) {
117044
117153
  for (const projectDir of projectDirs) {
117045
117154
  if (!projectDir.isDirectory())
117046
117155
  continue;
117047
- const projectPath = join81(SESSION_STORAGE, projectDir.name);
117156
+ const projectPath = join82(SESSION_STORAGE, projectDir.name);
117048
117157
  const sessionFiles = await readdir(projectPath);
117049
117158
  for (const file3 of sessionFiles) {
117050
117159
  if (!file3.endsWith(".json"))
117051
117160
  continue;
117052
117161
  try {
117053
- const content = await readFile(join81(projectPath, file3), "utf-8");
117162
+ const content = await readFile(join82(projectPath, file3), "utf-8");
117054
117163
  const meta3 = JSON.parse(content);
117055
117164
  if (meta3.parentID)
117056
117165
  continue;
@@ -117077,7 +117186,7 @@ async function getFileAllSessions() {
117077
117186
  for (const entry of entries) {
117078
117187
  if (!entry.isDirectory())
117079
117188
  continue;
117080
- const sessionPath = join81(dir, entry.name);
117189
+ const sessionPath = join82(dir, entry.name);
117081
117190
  const files = await readdir(sessionPath);
117082
117191
  if (files.some((file3) => file3.endsWith(".json"))) {
117083
117192
  sessions.push(entry.name);
@@ -117106,7 +117215,7 @@ async function getFileSessionMessages(sessionID) {
117106
117215
  if (!file3.endsWith(".json"))
117107
117216
  continue;
117108
117217
  try {
117109
- const content = await readFile(join81(messageDir, file3), "utf-8");
117218
+ const content = await readFile(join82(messageDir, file3), "utf-8");
117110
117219
  const meta3 = JSON.parse(content);
117111
117220
  const parts = await readParts2(meta3.id);
117112
117221
  messages.push({
@@ -117132,7 +117241,7 @@ async function getFileSessionMessages(sessionID) {
117132
117241
  });
117133
117242
  }
117134
117243
  async function readParts2(messageID) {
117135
- const partDir = join81(PART_STORAGE, messageID);
117244
+ const partDir = join82(PART_STORAGE, messageID);
117136
117245
  if (!existsSync78(partDir))
117137
117246
  return [];
117138
117247
  const parts = [];
@@ -117142,7 +117251,7 @@ async function readParts2(messageID) {
117142
117251
  if (!file3.endsWith(".json"))
117143
117252
  continue;
117144
117253
  try {
117145
- const content = await readFile(join81(partDir, file3), "utf-8");
117254
+ const content = await readFile(join82(partDir, file3), "utf-8");
117146
117255
  parts.push(JSON.parse(content));
117147
117256
  } catch {
117148
117257
  continue;
@@ -117161,7 +117270,7 @@ async function getFileSessionTodos(sessionID) {
117161
117270
  const todoFiles = allFiles.filter((file3) => file3 === `${sessionID}.json`);
117162
117271
  for (const file3 of todoFiles) {
117163
117272
  try {
117164
- const content = await readFile(join81(TODO_DIR2, file3), "utf-8");
117273
+ const content = await readFile(join82(TODO_DIR2, file3), "utf-8");
117165
117274
  const data = JSON.parse(content);
117166
117275
  if (!Array.isArray(data))
117167
117276
  continue;
@@ -117183,7 +117292,7 @@ async function getFileSessionTodos(sessionID) {
117183
117292
  async function getFileSessionTranscript(sessionID) {
117184
117293
  if (!existsSync78(TRANSCRIPT_DIR2))
117185
117294
  return 0;
117186
- const transcriptFile = join81(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
117295
+ const transcriptFile = join82(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
117187
117296
  if (!existsSync78(transcriptFile))
117188
117297
  return 0;
117189
117298
  try {
@@ -117349,6 +117458,19 @@ function shouldFallbackFromSdkError(error92) {
117349
117458
  }
117350
117459
 
117351
117460
  // src/tools/session-manager/storage.ts
117461
+ function mergeSessionMetadataLists(sdkSessions, fileSessions) {
117462
+ const merged = new Map;
117463
+ for (const session of fileSessions) {
117464
+ merged.set(session.id, session);
117465
+ }
117466
+ for (const session of sdkSessions) {
117467
+ merged.set(session.id, session);
117468
+ }
117469
+ return [...merged.values()].sort((a, b) => b.time.updated - a.time.updated);
117470
+ }
117471
+ function mergeSessionIds(sdkSessionIds, fileSessionIds) {
117472
+ return [...new Set([...sdkSessionIds, ...fileSessionIds])];
117473
+ }
117352
117474
  var sdkClient = null;
117353
117475
  function setStorageClient(client2) {
117354
117476
  sdkClient = client2;
@@ -117356,7 +117478,9 @@ function setStorageClient(client2) {
117356
117478
  async function getMainSessions(options) {
117357
117479
  if (isSqliteBackend() && sdkClient) {
117358
117480
  try {
117359
- return await getSdkMainSessions(sdkClient, options.directory);
117481
+ const sdkSessions = await getSdkMainSessions(sdkClient, options.directory);
117482
+ const fileSessions = await getFileMainSessions(options.directory);
117483
+ return mergeSessionMetadataLists(sdkSessions, fileSessions);
117360
117484
  } catch (error92) {
117361
117485
  if (!shouldFallbackFromSdkError(error92))
117362
117486
  throw error92;
@@ -117368,7 +117492,9 @@ async function getMainSessions(options) {
117368
117492
  async function getAllSessions() {
117369
117493
  if (isSqliteBackend() && sdkClient) {
117370
117494
  try {
117371
- return await getSdkAllSessions(sdkClient);
117495
+ const sdkSessionIds = await getSdkAllSessions(sdkClient);
117496
+ const fileSessionIds = await getFileAllSessions();
117497
+ return mergeSessionIds(sdkSessionIds, fileSessionIds);
117372
117498
  } catch (error92) {
117373
117499
  if (!shouldFallbackFromSdkError(error92))
117374
117500
  throw error92;
@@ -117380,7 +117506,9 @@ async function getAllSessions() {
117380
117506
  async function sessionExists2(sessionID) {
117381
117507
  if (isSqliteBackend() && sdkClient) {
117382
117508
  try {
117383
- return await sdkSessionExists(sdkClient, sessionID);
117509
+ const existsInSdk = await sdkSessionExists(sdkClient, sessionID);
117510
+ if (existsInSdk)
117511
+ return true;
117384
117512
  } catch (error92) {
117385
117513
  if (!shouldFallbackFromSdkError(error92))
117386
117514
  throw error92;
@@ -117392,7 +117520,9 @@ async function sessionExists2(sessionID) {
117392
117520
  async function readSessionMessages2(sessionID) {
117393
117521
  if (isSqliteBackend() && sdkClient) {
117394
117522
  try {
117395
- return await getSdkSessionMessages(sdkClient, sessionID);
117523
+ const sdkMessages = await getSdkSessionMessages(sdkClient, sessionID);
117524
+ if (sdkMessages.length > 0)
117525
+ return sdkMessages;
117396
117526
  } catch (error92) {
117397
117527
  if (!shouldFallbackFromSdkError(error92))
117398
117528
  throw error92;
@@ -117404,7 +117534,9 @@ async function readSessionMessages2(sessionID) {
117404
117534
  async function readSessionTodos(sessionID) {
117405
117535
  if (isSqliteBackend() && sdkClient) {
117406
117536
  try {
117407
- return await getSdkSessionTodos(sdkClient, sessionID);
117537
+ const sdkTodos = await getSdkSessionTodos(sdkClient, sessionID);
117538
+ if (sdkTodos.length > 0)
117539
+ return sdkTodos;
117408
117540
  } catch (error92) {
117409
117541
  if (!shouldFallbackFromSdkError(error92))
117410
117542
  throw error92;
@@ -117413,7 +117545,48 @@ async function readSessionTodos(sessionID) {
117413
117545
  }
117414
117546
  return getFileSessionTodos(sessionID);
117415
117547
  }
117548
+ async function readSessionTranscript(sessionID) {
117549
+ return getFileSessionTranscript(sessionID);
117550
+ }
117416
117551
  async function getSessionInfo(sessionID) {
117552
+ if (isSqliteBackend() && sdkClient) {
117553
+ try {
117554
+ const sdkMessages = await getSdkSessionMessages(sdkClient, sessionID);
117555
+ if (sdkMessages.length > 0) {
117556
+ const agentsUsed = new Set;
117557
+ let firstMessage;
117558
+ let lastMessage;
117559
+ for (const msg of sdkMessages) {
117560
+ if (msg.agent)
117561
+ agentsUsed.add(msg.agent);
117562
+ if (msg.time?.created) {
117563
+ const date9 = new Date(msg.time.created);
117564
+ if (!firstMessage || date9 < firstMessage)
117565
+ firstMessage = date9;
117566
+ if (!lastMessage || date9 > lastMessage)
117567
+ lastMessage = date9;
117568
+ }
117569
+ }
117570
+ const todos = await readSessionTodos(sessionID);
117571
+ const transcriptEntries = await readSessionTranscript(sessionID);
117572
+ return {
117573
+ id: sessionID,
117574
+ message_count: sdkMessages.length,
117575
+ first_message: firstMessage,
117576
+ last_message: lastMessage,
117577
+ agents_used: Array.from(agentsUsed),
117578
+ has_todos: todos.length > 0,
117579
+ has_transcript: transcriptEntries > 0,
117580
+ todos,
117581
+ transcript_entries: transcriptEntries
117582
+ };
117583
+ }
117584
+ } catch (error92) {
117585
+ if (!shouldFallbackFromSdkError(error92))
117586
+ throw error92;
117587
+ log("[session-manager] falling back to file session info after SDK unavailable error", { error: String(error92), sessionID });
117588
+ }
117589
+ }
117417
117590
  return getFileSessionInfo(sessionID);
117418
117591
  }
117419
117592
 
@@ -119396,7 +119569,7 @@ async function resolveMultimodalLookerAgentMetadata(ctx) {
119396
119569
  import * as childProcess from "child_process";
119397
119570
  import { existsSync as existsSync79, mkdtempSync, readFileSync as readFileSync51, rmSync as rmSync4, unlinkSync as unlinkSync11, writeFileSync as writeFileSync20 } from "fs";
119398
119571
  import { tmpdir as tmpdir7 } from "os";
119399
- import { dirname as dirname26, join as join82 } from "path";
119572
+ import { dirname as dirname27, join as join83 } from "path";
119400
119573
  var SUPPORTED_FORMATS = new Set([
119401
119574
  "image/jpeg",
119402
119575
  "image/png",
@@ -119437,8 +119610,8 @@ function convertImageToJpeg(inputPath, mimeType) {
119437
119610
  if (!existsSync79(inputPath)) {
119438
119611
  throw new Error(`File not found: ${inputPath}`);
119439
119612
  }
119440
- const tempDir = mkdtempSync(join82(tmpdir7(), "opencode-img-"));
119441
- const outputPath = join82(tempDir, "converted.jpg");
119613
+ const tempDir = mkdtempSync(join83(tmpdir7(), "opencode-img-"));
119614
+ const outputPath = join83(tempDir, "converted.jpg");
119442
119615
  log(`[image-converter] Converting ${mimeType} to JPEG: ${inputPath}`);
119443
119616
  try {
119444
119617
  if (process.platform === "darwin") {
@@ -119489,7 +119662,7 @@ function convertImageToJpeg(inputPath, mimeType) {
119489
119662
  }
119490
119663
  function cleanupConvertedImage(filePath) {
119491
119664
  try {
119492
- const tempDirectory = dirname26(filePath);
119665
+ const tempDirectory = dirname27(filePath);
119493
119666
  if (existsSync79(filePath)) {
119494
119667
  unlinkSync11(filePath);
119495
119668
  log(`[image-converter] Cleaned up temporary file: ${filePath}`);
@@ -119503,9 +119676,9 @@ function cleanupConvertedImage(filePath) {
119503
119676
  }
119504
119677
  }
119505
119678
  function convertBase64ImageToJpeg(base64Data, mimeType) {
119506
- const tempDir = mkdtempSync(join82(tmpdir7(), "opencode-b64-"));
119679
+ const tempDir = mkdtempSync(join83(tmpdir7(), "opencode-b64-"));
119507
119680
  const inputExt = mimeType.split("/")[1] || "bin";
119508
- const inputPath = join82(tempDir, `input.${inputExt}`);
119681
+ const inputPath = join83(tempDir, `input.${inputExt}`);
119509
119682
  const tempFiles = [inputPath];
119510
119683
  try {
119511
119684
  const cleanBase64 = base64Data.replace(/^data:[^;]+;base64,/, "");
@@ -120062,6 +120235,7 @@ function getTimingConfig() {
120062
120235
  // src/tools/delegate-task/sync-session-poller.ts
120063
120236
  init_logger();
120064
120237
  var NON_TERMINAL_FINISH_REASONS = new Set(["tool-calls", "unknown"]);
120238
+ var PENDING_TOOL_PART_TYPES = new Set(["tool", "tool_use", "tool-call"]);
120065
120239
  function wait(milliseconds) {
120066
120240
  const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
120067
120241
  const typedArray = new Int32Array(sharedBuffer);
@@ -120094,6 +120268,8 @@ function isSessionComplete(messages) {
120094
120268
  return false;
120095
120269
  if (NON_TERMINAL_FINISH_REASONS.has(lastAssistant.info.finish))
120096
120270
  return false;
120271
+ if (lastAssistant.parts?.some((part) => part.type && PENDING_TOOL_PART_TYPES.has(part.type)))
120272
+ return false;
120097
120273
  if (!lastUser?.info?.id || !lastAssistant?.info?.id)
120098
120274
  return false;
120099
120275
  return lastUser.info.id < lastAssistant.info.id;
@@ -121789,7 +121965,7 @@ function createDelegateTask(options) {
121789
121965
  // src/tools/delegate-task/index.ts
121790
121966
  init_constants();
121791
121967
  // src/tools/task/task-create.ts
121792
- import { join as join84 } from "path";
121968
+ import { join as join85 } from "path";
121793
121969
 
121794
121970
  // src/tools/task/types.ts
121795
121971
  var TaskStatusSchema = exports_external.enum(["pending", "in_progress", "completed", "deleted"]);
@@ -121843,18 +122019,18 @@ var TaskDeleteInputSchema = exports_external.object({
121843
122019
  });
121844
122020
 
121845
122021
  // src/features/claude-tasks/storage.ts
121846
- import { join as join83, dirname as dirname27, basename as basename13, isAbsolute as isAbsolute11 } from "path";
121847
- import { existsSync as existsSync80, mkdirSync as mkdirSync15, readFileSync as readFileSync52, writeFileSync as writeFileSync21, renameSync as renameSync5, unlinkSync as unlinkSync12, readdirSync as readdirSync20 } from "fs";
122022
+ import { join as join84, dirname as dirname28, basename as basename13, isAbsolute as isAbsolute11 } from "path";
122023
+ import { existsSync as existsSync80, mkdirSync as mkdirSync16, readFileSync as readFileSync52, writeFileSync as writeFileSync21, renameSync as renameSync5, unlinkSync as unlinkSync12, readdirSync as readdirSync20 } from "fs";
121848
122024
  import { randomUUID as randomUUID3 } from "crypto";
121849
122025
  function getTaskDir(config4 = {}) {
121850
122026
  const tasksConfig = config4.sisyphus?.tasks;
121851
122027
  const storagePath = tasksConfig?.storage_path;
121852
122028
  if (storagePath) {
121853
- return isAbsolute11(storagePath) ? storagePath : join83(process.cwd(), storagePath);
122029
+ return isAbsolute11(storagePath) ? storagePath : join84(process.cwd(), storagePath);
121854
122030
  }
121855
122031
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
121856
122032
  const listId = resolveTaskListId(config4);
121857
- return join83(configDir, "tasks", listId);
122033
+ return join84(configDir, "tasks", listId);
121858
122034
  }
121859
122035
  function sanitizePathSegment(value) {
121860
122036
  return value.replace(/[^a-zA-Z0-9_-]/g, "-") || "default";
@@ -121873,7 +122049,7 @@ function resolveTaskListId(config4 = {}) {
121873
122049
  }
121874
122050
  function ensureDir(dirPath) {
121875
122051
  if (!existsSync80(dirPath)) {
121876
- mkdirSync15(dirPath, { recursive: true });
122052
+ mkdirSync16(dirPath, { recursive: true });
121877
122053
  }
121878
122054
  }
121879
122055
  function readJsonSafe(filePath, schema2) {
@@ -121893,7 +122069,7 @@ function readJsonSafe(filePath, schema2) {
121893
122069
  }
121894
122070
  }
121895
122071
  function writeJsonAtomic(filePath, data) {
121896
- const dir = dirname27(filePath);
122072
+ const dir = dirname28(filePath);
121897
122073
  ensureDir(dir);
121898
122074
  const tempPath = `${filePath}.tmp.${Date.now()}`;
121899
122075
  try {
@@ -121913,7 +122089,7 @@ function generateTaskId() {
121913
122089
  return `T-${randomUUID3()}`;
121914
122090
  }
121915
122091
  function acquireLock(dirPath) {
121916
- const lockPath = join83(dirPath, ".lock");
122092
+ const lockPath = join84(dirPath, ".lock");
121917
122093
  const lockId = randomUUID3();
121918
122094
  const createLock = (timestamp2) => {
121919
122095
  writeFileSync21(lockPath, JSON.stringify({ id: lockId, timestamp: timestamp2 }), {
@@ -122126,7 +122302,7 @@ async function handleCreate(args, config4, ctx, context) {
122126
122302
  threadID: context.sessionID
122127
122303
  };
122128
122304
  const validatedTask = TaskObjectSchema.parse(task);
122129
- writeJsonAtomic(join84(taskDir, `${taskId}.json`), validatedTask);
122305
+ writeJsonAtomic(join85(taskDir, `${taskId}.json`), validatedTask);
122130
122306
  await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
122131
122307
  return JSON.stringify({
122132
122308
  task: {
@@ -122148,7 +122324,7 @@ async function handleCreate(args, config4, ctx, context) {
122148
122324
  }
122149
122325
  }
122150
122326
  // src/tools/task/task-get.ts
122151
- import { join as join85 } from "path";
122327
+ import { join as join86 } from "path";
122152
122328
  var TASK_ID_PATTERN = /^T-[A-Za-z0-9-]+$/;
122153
122329
  function parseTaskId(id) {
122154
122330
  if (!TASK_ID_PATTERN.test(id))
@@ -122173,7 +122349,7 @@ Returns null if the task does not exist or the file is invalid.`,
122173
122349
  return JSON.stringify({ error: "invalid_task_id" });
122174
122350
  }
122175
122351
  const taskDir = getTaskDir(config4);
122176
- const taskPath = join85(taskDir, `${taskId}.json`);
122352
+ const taskPath = join86(taskDir, `${taskId}.json`);
122177
122353
  const task = readJsonSafe(taskPath, TaskObjectSchema);
122178
122354
  return JSON.stringify({ task: task ?? null });
122179
122355
  } catch (error92) {
@@ -122186,7 +122362,7 @@ Returns null if the task does not exist or the file is invalid.`,
122186
122362
  });
122187
122363
  }
122188
122364
  // src/tools/task/task-list.ts
122189
- import { join as join86 } from "path";
122365
+ import { join as join87 } from "path";
122190
122366
  import { existsSync as existsSync81, readdirSync as readdirSync21 } from "fs";
122191
122367
  function createTaskList(config4) {
122192
122368
  return tool({
@@ -122207,7 +122383,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
122207
122383
  }
122208
122384
  const allTasks = [];
122209
122385
  for (const fileId of files) {
122210
- const task = readJsonSafe(join86(taskDir, `${fileId}.json`), TaskObjectSchema);
122386
+ const task = readJsonSafe(join87(taskDir, `${fileId}.json`), TaskObjectSchema);
122211
122387
  if (task) {
122212
122388
  allTasks.push(task);
122213
122389
  }
@@ -122235,7 +122411,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
122235
122411
  });
122236
122412
  }
122237
122413
  // src/tools/task/task-update.ts
122238
- import { join as join87 } from "path";
122414
+ import { join as join88 } from "path";
122239
122415
  var TASK_ID_PATTERN2 = /^T-[A-Za-z0-9-]+$/;
122240
122416
  function parseTaskId2(id) {
122241
122417
  if (!TASK_ID_PATTERN2.test(id))
@@ -122283,7 +122459,7 @@ async function handleUpdate(args, config4, ctx, context) {
122283
122459
  return JSON.stringify({ error: "task_lock_unavailable" });
122284
122460
  }
122285
122461
  try {
122286
- const taskPath = join87(taskDir, `${taskId}.json`);
122462
+ const taskPath = join88(taskDir, `${taskId}.json`);
122287
122463
  const task = readJsonSafe(taskPath, TaskObjectSchema);
122288
122464
  if (!task) {
122289
122465
  return JSON.stringify({ error: "task_not_found" });
@@ -123947,6 +124123,9 @@ var builtinTools = {
123947
124123
  function isTmuxIntegrationEnabled(pluginConfig) {
123948
124124
  return pluginConfig.tmux?.enabled ?? false;
123949
124125
  }
124126
+ function isInteractiveBashEnabled(which = Bun.which) {
124127
+ return which("tmux") !== null;
124128
+ }
123950
124129
  function createRuntimeTmuxConfig(pluginConfig) {
123951
124130
  return TmuxConfigSchema.parse(pluginConfig.tmux ?? {});
123952
124131
  }
@@ -124495,6 +124674,40 @@ function createHooks(args) {
124495
124674
  }
124496
124675
  };
124497
124676
  }
124677
+ // src/features/background-agent/constants.ts
124678
+ var TASK_TTL_MS = 30 * 60 * 1000;
124679
+ var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
124680
+ var MIN_STABILITY_TIME_MS2 = 10 * 1000;
124681
+ var DEFAULT_STALE_TIMEOUT_MS = 2700000;
124682
+ var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
124683
+ var DEFAULT_MAX_TOOL_CALLS = 4000;
124684
+ var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
124685
+ var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
124686
+ var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
124687
+ var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
124688
+ var MIN_IDLE_TIME_MS = 5000;
124689
+ var POLLING_INTERVAL_MS = 3000;
124690
+ var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
124691
+
124692
+ // src/features/background-agent/spawner.ts
124693
+ var FALLBACK_AGENT = "general";
124694
+ function isAgentNotFoundError(error92) {
124695
+ const message = typeof error92 === "string" ? error92 : error92 instanceof Error ? error92.message : typeof error92 === "object" && error92 !== null && typeof error92.message === "string" ? error92.message : String(error92);
124696
+ return message.includes("Agent not found") || message.includes("agent.name");
124697
+ }
124698
+ function buildFallbackBody(originalBody, fallbackAgent) {
124699
+ return {
124700
+ ...originalBody,
124701
+ agent: fallbackAgent,
124702
+ tools: {
124703
+ task: false,
124704
+ call_omo_agent: true,
124705
+ question: false,
124706
+ ...getAgentToolRestrictions(fallbackAgent)
124707
+ }
124708
+ };
124709
+ }
124710
+
124498
124711
  // src/features/background-agent/task-history.ts
124499
124712
  var MAX_ENTRIES_PER_PARENT = 100;
124500
124713
 
@@ -124652,21 +124865,6 @@ class ConcurrencyManager {
124652
124865
  }
124653
124866
  }
124654
124867
 
124655
- // src/features/background-agent/constants.ts
124656
- var TASK_TTL_MS = 30 * 60 * 1000;
124657
- var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
124658
- var MIN_STABILITY_TIME_MS2 = 10 * 1000;
124659
- var DEFAULT_STALE_TIMEOUT_MS = 2700000;
124660
- var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
124661
- var DEFAULT_MAX_TOOL_CALLS = 4000;
124662
- var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
124663
- var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
124664
- var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
124665
- var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
124666
- var MIN_IDLE_TIME_MS = 5000;
124667
- var POLLING_INTERVAL_MS = 3000;
124668
- var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
124669
-
124670
124868
  // src/features/background-agent/duration-formatter.ts
124671
124869
  function formatDuration3(start, end) {
124672
124870
  const duration5 = (end ?? new Date).getTime() - start.getTime();
@@ -125005,7 +125203,7 @@ function unregisterManagerForCleanup(manager) {
125005
125203
 
125006
125204
  // src/features/background-agent/compaction-aware-message-resolver.ts
125007
125205
  import { readdirSync as readdirSync22, readFileSync as readFileSync53 } from "fs";
125008
- import { join as join88 } from "path";
125206
+ import { join as join89 } from "path";
125009
125207
  function isCompactionAgent4(agent) {
125010
125208
  return agent?.trim().toLowerCase() === "compaction";
125011
125209
  }
@@ -125084,7 +125282,7 @@ function findNearestMessageExcludingCompaction(messageDir, sessionID) {
125084
125282
  const messages = [];
125085
125283
  for (const file3 of files) {
125086
125284
  try {
125087
- const content = readFileSync53(join88(messageDir, file3), "utf-8");
125285
+ const content = readFileSync53(join89(messageDir, file3), "utf-8");
125088
125286
  messages.push(JSON.parse(content));
125089
125287
  } catch {
125090
125288
  continue;
@@ -125170,7 +125368,7 @@ function handleSessionIdleBackgroundEvent(args) {
125170
125368
  }
125171
125369
 
125172
125370
  // src/features/background-agent/manager.ts
125173
- import { join as join89 } from "path";
125371
+ import { join as join90 } from "path";
125174
125372
 
125175
125373
  // src/features/background-agent/remove-task-toast-tracking.ts
125176
125374
  function removeTaskToastTracking(taskId) {
@@ -125858,32 +126056,52 @@ class BackgroundManager {
125858
126056
  if (input.model) {
125859
126057
  applySessionPromptParams(sessionID, input.model);
125860
126058
  }
126059
+ const promptBody = {
126060
+ agent: input.agent,
126061
+ ...launchModel ? { model: launchModel } : {},
126062
+ ...launchVariant ? { variant: launchVariant } : {},
126063
+ system: input.skillContent,
126064
+ tools: (() => {
126065
+ const tools = {
126066
+ task: false,
126067
+ call_omo_agent: true,
126068
+ question: false,
126069
+ ...getAgentToolRestrictions(input.agent)
126070
+ };
126071
+ setSessionTools(sessionID, tools);
126072
+ return tools;
126073
+ })(),
126074
+ parts: [createInternalAgentTextPart(input.prompt)]
126075
+ };
125861
126076
  promptWithModelSuggestionRetry(this.client, {
125862
126077
  path: { id: sessionID },
125863
- body: {
125864
- agent: input.agent,
125865
- ...launchModel ? { model: launchModel } : {},
125866
- ...launchVariant ? { variant: launchVariant } : {},
125867
- system: input.skillContent,
125868
- tools: (() => {
125869
- const tools = {
125870
- task: false,
125871
- call_omo_agent: true,
125872
- question: false,
125873
- ...getAgentToolRestrictions(input.agent)
125874
- };
125875
- setSessionTools(sessionID, tools);
125876
- return tools;
125877
- })(),
125878
- parts: [createInternalAgentTextPart(input.prompt)]
125879
- }
126078
+ body: promptBody
125880
126079
  }).catch(async (error92) => {
126080
+ if (isAgentNotFoundError(error92) && input.agent !== FALLBACK_AGENT) {
126081
+ log("[background-agent] Agent not found, retrying with fallback agent", {
126082
+ original: input.agent,
126083
+ fallback: FALLBACK_AGENT,
126084
+ taskId: task.id
126085
+ });
126086
+ try {
126087
+ const fallbackBody = buildFallbackBody(promptBody, FALLBACK_AGENT);
126088
+ setSessionTools(sessionID, fallbackBody.tools);
126089
+ await promptWithModelSuggestionRetry(this.client, {
126090
+ path: { id: sessionID },
126091
+ body: fallbackBody
126092
+ });
126093
+ task.agent = FALLBACK_AGENT;
126094
+ return;
126095
+ } catch (retryError) {
126096
+ log("[background-agent] Fallback agent also failed:", retryError);
126097
+ }
126098
+ }
125881
126099
  log("[background-agent] promptAsync error:", error92);
125882
126100
  const existingTask = this.findBySession(sessionID);
125883
126101
  if (existingTask) {
125884
126102
  existingTask.status = "interrupt";
125885
126103
  const errorMessage = error92 instanceof Error ? error92.message : String(error92);
125886
- if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
126104
+ if (errorMessage.includes("agent.name") || errorMessage.includes("undefined") || isAgentNotFoundError(error92)) {
125887
126105
  existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
125888
126106
  } else {
125889
126107
  existingTask.error = errorMessage;
@@ -126394,6 +126612,13 @@ class BackgroundManager {
126394
126612
  }
126395
126613
  async handleSessionErrorEvent(args) {
126396
126614
  const { task, errorInfo, errorMessage, errorName } = args;
126615
+ if (isAgentNotFoundError({ message: errorInfo.message })) {
126616
+ log("[background-agent] Skipping session.error fallback for agent-not-found (handled by prompt catch)", {
126617
+ taskId: task.id,
126618
+ errorMessage: errorInfo.message?.slice(0, 100)
126619
+ });
126620
+ return;
126621
+ }
126397
126622
  if (await this.tryFallbackRetry(task, errorInfo, "session.error")) {
126398
126623
  return;
126399
126624
  }
@@ -126797,7 +127022,7 @@ ${originalText}`;
126797
127022
  parentSessionID: task.parentSessionID
126798
127023
  });
126799
127024
  }
126800
- const messageDir = join89(MESSAGE_STORAGE, task.parentSessionID);
127025
+ const messageDir = join90(MESSAGE_STORAGE, task.parentSessionID);
126801
127026
  const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir, task.parentSessionID) : null;
126802
127027
  agent = currentMessage?.agent ?? task.parentAgent;
126803
127028
  model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
@@ -127125,11 +127350,11 @@ ${originalText}`;
127125
127350
  }
127126
127351
  }
127127
127352
  // src/features/mcp-oauth/storage.ts
127128
- import { chmodSync as chmodSync2, existsSync as existsSync82, mkdirSync as mkdirSync16, readFileSync as readFileSync54, unlinkSync as unlinkSync13, writeFileSync as writeFileSync22 } from "fs";
127129
- import { dirname as dirname28, join as join90 } from "path";
127353
+ import { chmodSync as chmodSync2, existsSync as existsSync82, mkdirSync as mkdirSync17, readFileSync as readFileSync54, unlinkSync as unlinkSync13, writeFileSync as writeFileSync22 } from "fs";
127354
+ import { dirname as dirname29, join as join91 } from "path";
127130
127355
  var STORAGE_FILE_NAME = "mcp-oauth.json";
127131
127356
  function getMcpOauthStoragePath() {
127132
- return join90(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
127357
+ return join91(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
127133
127358
  }
127134
127359
  function normalizeHost(serverHost) {
127135
127360
  let host = serverHost.trim();
@@ -127179,9 +127404,9 @@ function readStore() {
127179
127404
  function writeStore(store2) {
127180
127405
  const filePath = getMcpOauthStoragePath();
127181
127406
  try {
127182
- const dir = dirname28(filePath);
127407
+ const dir = dirname29(filePath);
127183
127408
  if (!existsSync82(dir)) {
127184
- mkdirSync16(dir, { recursive: true });
127409
+ mkdirSync17(dir, { recursive: true });
127185
127410
  }
127186
127411
  writeFileSync22(filePath, JSON.stringify(store2, null, 2), { encoding: "utf-8", mode: 384 });
127187
127412
  chmodSync2(filePath, 384);
@@ -133676,17 +133901,17 @@ var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
133676
133901
  var MIN_STABILITY_TIME_MS4 = 10 * 1000;
133677
133902
  // src/features/claude-code-mcp-loader/loader.ts
133678
133903
  import { existsSync as existsSync83, readFileSync as readFileSync55 } from "fs";
133679
- import { join as join91 } from "path";
133904
+ import { join as join92 } from "path";
133680
133905
  import { homedir as homedir15 } from "os";
133681
133906
  init_logger();
133682
133907
  function getMcpConfigPaths() {
133683
133908
  const claudeConfigDir = getClaudeConfigDir();
133684
133909
  const cwd = process.cwd();
133685
133910
  return [
133686
- { path: join91(homedir15(), ".claude.json"), scope: "user" },
133687
- { path: join91(claudeConfigDir, ".mcp.json"), scope: "user" },
133688
- { path: join91(cwd, ".mcp.json"), scope: "project" },
133689
- { path: join91(cwd, ".claude", ".mcp.json"), scope: "local" }
133911
+ { path: join92(homedir15(), ".claude.json"), scope: "user" },
133912
+ { path: join92(claudeConfigDir, ".mcp.json"), scope: "user" },
133913
+ { path: join92(cwd, ".mcp.json"), scope: "project" },
133914
+ { path: join92(cwd, ".claude", ".mcp.json"), scope: "local" }
133690
133915
  ];
133691
133916
  }
133692
133917
  async function loadMcpConfigFile(filePath) {
@@ -138716,43 +138941,39 @@ function buildTodoDisciplineSection3(useTaskSystem) {
138716
138941
  if (useTaskSystem) {
138717
138942
  return `## Task Discipline (NON-NEGOTIABLE)
138718
138943
 
138719
- Track ALL multi-step work with tasks. This is your execution backbone.
138944
+ **Track ALL multi-step work with tasks. This is your execution backbone.**
138720
138945
 
138721
138946
  ### When to Create Tasks (MANDATORY)
138722
138947
 
138723
- - 2+ step task - \`task_create\` FIRST, atomic breakdown
138724
- - Uncertain scope - \`task_create\` to clarify thinking
138725
- - Complex single task - break down into trackable steps
138948
+ - **2+ step task** - \`task_create\` FIRST, atomic breakdown
138949
+ - **Uncertain scope** - \`task_create\` to clarify thinking
138950
+ - **Complex single task** - Break down into trackable steps
138726
138951
 
138727
138952
  ### Workflow (STRICT)
138728
138953
 
138729
- 1. On task start: \`task_create\` with atomic steps - no announcements, just create
138730
- 2. Before each step: \`task_update(status="in_progress")\` (ONE at a time)
138731
- 3. After each step: \`task_update(status="completed")\` IMMEDIATELY (NEVER batch)
138732
- 4. Scope changes: update tasks BEFORE proceeding
138733
-
138734
- Tasks prevent drift, enable recovery if interrupted, and make each commitment explicit. Skipping tasks on multi-step work, batch-completing, or proceeding without \`in_progress\` are blocking violations.
138954
+ 1. **On task start**: \`task_create\` with atomic steps-no announcements, just create
138955
+ 2. **Before each step**: \`task_update(status="in_progress")\` (ONE at a time)
138956
+ 3. **After each step**: \`task_update(status="completed")\` IMMEDIATELY (NEVER batch)
138957
+ 4. **Scope changes**: Update tasks BEFORE proceeding
138735
138958
 
138736
138959
  **NO TASKS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
138737
138960
  }
138738
138961
  return `## Todo Discipline (NON-NEGOTIABLE)
138739
138962
 
138740
- Track ALL multi-step work with todos. This is your execution backbone.
138963
+ **Track ALL multi-step work with todos. This is your execution backbone.**
138741
138964
 
138742
138965
  ### When to Create Todos (MANDATORY)
138743
138966
 
138744
- - 2+ step task - \`todowrite\` FIRST, atomic breakdown
138745
- - Uncertain scope - \`todowrite\` to clarify thinking
138746
- - Complex single task - break down into trackable steps
138967
+ - **2+ step task** - \`todowrite\` FIRST, atomic breakdown
138968
+ - **Uncertain scope** - \`todowrite\` to clarify thinking
138969
+ - **Complex single task** - Break down into trackable steps
138747
138970
 
138748
138971
  ### Workflow (STRICT)
138749
138972
 
138750
- 1. On task start: \`todowrite\` with atomic steps - no announcements, just create
138751
- 2. Before each step: mark \`in_progress\` (ONE at a time)
138752
- 3. After each step: mark \`completed\` IMMEDIATELY (NEVER batch)
138753
- 4. Scope changes: update todos BEFORE proceeding
138754
-
138755
- Todos prevent drift, enable recovery if interrupted, and make each commitment explicit. Skipping todos on multi-step work, batch-completing, or proceeding without \`in_progress\` are blocking violations.
138973
+ 1. **On task start**: \`todowrite\` with atomic steps-no announcements, just create
138974
+ 2. **Before each step**: Mark \`in_progress\` (ONE at a time)
138975
+ 3. **After each step**: Mark \`completed\` IMMEDIATELY (NEVER batch)
138976
+ 4. **Scope changes**: Update todos BEFORE proceeding
138756
138977
 
138757
138978
  **NO TODOS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
138758
138979
  }
@@ -138766,313 +138987,243 @@ function buildHephaestusPrompt3(availableAgents = [], availableTools = [], avail
138766
138987
  const oracleSection = buildOracleSection(availableAgents);
138767
138988
  const hardBlocks = buildHardBlocksSection();
138768
138989
  const antiPatterns = buildAntiPatternsSection();
138990
+ const antiDuplication = buildAntiDuplicationSection();
138769
138991
  const todoDiscipline = buildTodoDisciplineSection3(useTaskSystem);
138770
- return `You are Hephaestus, an autonomous deep worker for software engineering.
138771
-
138772
- ## Identity
138773
-
138774
- You build context by examining the codebase first without making assumptions. You think through the nuances of the code you encounter. You do not stop early. You complete.
138775
-
138776
- Persist until the task is fully handled end-to-end within the current turn. Persevere even when tool calls fail. Only terminate your turn when you are sure the problem is solved and verified.
138777
-
138778
- When blocked: try a different approach \u2192 decompose the problem \u2192 challenge assumptions \u2192 explore how others solved it. Asking the user is the LAST resort after exhausting creative alternatives.
138779
-
138780
- ### Do NOT Ask - Just Do
138781
-
138782
- **FORBIDDEN:**
138783
- - Asking permission in any form ("Should I proceed?", "Would you like me to...?", "I can do X if you want") \u2192 JUST DO IT.
138784
- - "Do you want me to run tests?" \u2192 RUN THEM.
138785
- - "I noticed Y, should I fix it?" \u2192 FIX IT OR NOTE IN FINAL MESSAGE.
138786
- - Stopping after partial implementation \u2192 100% OR NOTHING.
138787
- - Answering a question then stopping \u2192 The question implies action. DO THE ACTION.
138788
- - "I'll do X" / "I recommend X" then ending turn \u2192 You COMMITTED to X. DO X NOW before ending.
138789
- - Explaining findings without acting on them \u2192 ACT on your findings immediately.
138790
-
138791
- **CORRECT:**
138792
- - Keep going until COMPLETELY done
138793
- - Run verification (lint, tests, build) WITHOUT asking
138794
- - Make decisions. Course-correct only on CONCRETE failure
138795
- - Note assumptions in final message, not as questions mid-work
138796
- - Need context? Fire explore/librarian in background IMMEDIATELY - continue only with non-overlapping work while they search
138797
- - User asks "did you do X?" and you didn't \u2192 Acknowledge briefly, DO X immediately
138798
- - User asks a question implying work \u2192 Answer briefly, DO the implied work in the same turn
138799
- - You wrote a plan in your response \u2192 EXECUTE the plan before ending turn - plans are starting lines, not finish lines
138800
-
138801
- ### Task Scope Clarification
138992
+ const identityBlock = `<identity>
138993
+ You are Hephaestus, an autonomous deep worker for software engineering.
138802
138994
 
138803
- You handle multi-step sub-tasks of a SINGLE GOAL. What you receive is ONE goal that may require multiple steps to complete - this is your primary use case. Only reject when given MULTIPLE INDEPENDENT goals in one request.
138995
+ You communicate warmly and directly, like a senior colleague walking through a problem together. You explain the why behind decisions, not just the what. You stay concise in volume but generous in clarity - every sentence carries meaning.
138804
138996
 
138805
- ## Hard Constraints
138997
+ You build context by examining the codebase first without assumptions. You think through the nuances of the code you encounter. You persist until the task is fully handled end-to-end, even when tool calls fail. You only end your turn when the problem is solved and verified.
138806
138998
 
138807
- ${hardBlocks}
138999
+ You are autonomous. When you see work to do, do it - run tests, fix issues, make decisions. Course-correct only on concrete failure. State assumptions in your final message, not as questions along the way. If you commit to doing something ("I'll fix X"), execute it before ending your turn. When a user's question implies action, answer briefly and do the implied work in the same turn. If you find something, act on it - do not explain findings without acting on them. Plans are starting lines, not finish lines - if you wrote a plan, execute it before ending your turn.
138808
139000
 
138809
- ${antiPatterns}
138810
-
138811
- ## Phase 0 - Intent Gate (EVERY task)
139001
+ When blocked: try a different approach, decompose the problem, challenge your assumptions, explore how others solved it. Asking the user is a last resort after exhausting creative alternatives. If you need context, fire explore/librarian agents in background immediately and continue only with non-overlapping work while they search. Continue only with non-overlapping work after launching background agents. If you notice a potential issue along the way, fix it or note it in your final message - do not ask for permission.
138812
139002
 
139003
+ You handle multi-step sub-tasks of a single goal. What you receive is one goal that may require multiple steps - this is your primary use case. Only flag when given genuinely independent goals in one request.
139004
+ </identity>`;
139005
+ const intentBlock = `<intent>
138813
139006
  ${keyTriggers}
138814
139007
 
138815
- <intent_extraction>
138816
- ### Step 0: Extract True Intent (BEFORE Classification)
139008
+ You are an autonomous deep worker. Users chose you for ACTION, not analysis. Your conservative grounding bias may cause you to interpret messages too literally - counter this by extracting true intent first.
138817
139009
 
138818
- You are an autonomous deep worker. Users chose you for ACTION, not analysis.
139010
+ Every message has a surface form and a true intent. Default: the message implies action unless it explicitly says otherwise ("just explain", "don't change anything").
138819
139011
 
138820
- Every user message has a surface form and a true intent. Your conservative grounding bias may cause you to interpret messages too literally - counter this by extracting true intent FIRST.
138821
-
138822
- **Intent Mapping (act on TRUE intent, not surface form):**
138823
-
138824
- | Surface Form | True Intent | Your Response |
139012
+ <intent_mapping>
139013
+ | Surface Form | True Intent | Your Move |
138825
139014
  |---|---|---|
138826
- | "Did you do X?" (and you didn't) | You forgot X. Do it now. | Acknowledge \u2192 DO X immediately |
138827
- | "How does X work?" | Understand X to work with/fix it | Explore \u2192 Implement/Fix |
138828
- | "Can you look into Y?" | Investigate AND resolve Y | Investigate \u2192 Resolve |
138829
- | "What's the best way to do Z?" | Actually do Z the best way | Decide \u2192 Implement |
138830
- | "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose \u2192 Fix |
138831
- | "What do you think about C?" | Evaluate, decide, implement C | Evaluate \u2192 Implement best option |
138832
-
138833
- Pure question (NO action) ONLY when ALL of these are true: user explicitly says "just explain" / "don't change anything" / "I'm just curious", no actionable codebase context, and no problem or improvement is mentioned or implied.
138834
-
138835
- DEFAULT: Message implies action unless explicitly stated otherwise.
138836
-
138837
- Verbalize your classification before acting:
138838
-
138839
- > "I detect [implementation/fix/investigation/pure question] intent - [reason]. [Action I'm taking now]."
138840
-
138841
- This verbalization commits you to action. Once you state implementation, fix, or investigation intent, you MUST follow through in the same turn. Only "pure question" permits ending without action.
138842
- </intent_extraction>
138843
-
138844
- ### Step 1: Classify Task Type
138845
-
138846
- - **Trivial**: Single file, known location, <10 lines - Direct tools only (UNLESS Key Trigger applies)
138847
- - **Explicit**: Specific file/line, clear command - Execute directly
138848
- - **Exploratory**: "How does X work?", "Find Y" - Fire explore (1-3) + tools in parallel \u2192 then ACT on findings (see Step 0 true intent)
138849
- - **Open-ended**: "Improve", "Refactor", "Add feature" - Full Execution Loop required
138850
- - **Ambiguous**: Unclear scope, multiple interpretations - Ask ONE clarifying question
139015
+ | "Did you do X?" (and you didn't) | Do X now | Acknowledge briefly, do X |
139016
+ | "How does X work?" | Understand to fix/improve | Explore, then implement/fix |
139017
+ | "Can you look into Y?" | Investigate and resolve | Investigate, then resolve |
139018
+ | "What's the best way to do Z?" | Do Z the best way | Decide, then implement |
139019
+ | "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose, then fix |
139020
+ | "What do you think about C?" | Evaluate and implement | Evaluate, then implement best option |
139021
+ </intent_mapping>
138851
139022
 
138852
- ### Step 2: Ambiguity Protocol (EXPLORE FIRST - NEVER ask before exploring)
139023
+ Pure question (no action) only when ALL of these are true: user explicitly says "just explain" / "don't change anything", no actionable codebase context, and no problem or improvement is mentioned.
138853
139024
 
138854
- - Single valid interpretation - proceed immediately
138855
- - Missing info that MIGHT exist - EXPLORE FIRST with tools (\`gh\`, \`git\`, \`grep\`, explore agents)
138856
- - Multiple plausible interpretations - cover ALL likely intents comprehensively, don't ask
138857
- - Truly impossible to proceed - ask ONE precise question (LAST RESORT)
139025
+ State your read before acting: "I detect [intent type] - [reason]. [What I'm doing now]." This commits you to follow through in the same turn.
138858
139026
 
138859
- Exploration hierarchy (MANDATORY before any question):
138860
- 1. Direct tools: \`gh pr list\`, \`git log\`, \`grep\`, \`rg\`, file reads
139027
+ Complexity:
139028
+ - Trivial (single file, <10 lines) - direct tools, unless a key trigger fires
139029
+ - Explicit (specific file/line) - execute directly
139030
+ - Exploratory ("how does X work?") - fire explore agents + tools in parallel, then act on findings
139031
+ - Open-ended ("improve", "refactor") - full execution loop
139032
+ - Ambiguous - explore first, cover all likely intents comprehensively rather than asking
139033
+ - Uncertain scope - create todos to clarify thinking, then proceed
139034
+
139035
+ Before asking the user anything, exhaust this hierarchy:
139036
+ 1. Direct tools: \`grep\`, \`rg\`, file reads, \`gh\`, \`git log\`
138861
139037
  2. Explore agents: fire 2-3 parallel background searches
138862
139038
  3. Librarian agents: check docs, GitHub, external sources
138863
139039
  4. Context inference: educated guess from surrounding context
138864
- 5. LAST RESORT: ask ONE precise question (only if 1-4 all failed)
138865
-
138866
- If you notice a potential issue - fix it or note it in final message. Don't ask for permission.
138867
-
138868
- ### Step 3: Validate Before Acting
139040
+ 5. Only when 1-4 all fail: ask one precise question
138869
139041
 
138870
- **Assumptions Check:** Do I have implicit assumptions? Is the search scope clear?
139042
+ Before acting, check:
139043
+ - Do I have implicit assumptions? Is the search scope clear?
139044
+ - Is there a skill whose domain overlaps? Load it immediately.
139045
+ - Is there a specialized agent that matches this? What category + skills to equip?
139046
+ - Can I do it myself for the best result? Default to delegation for complex tasks.
138871
139047
 
138872
- **Delegation Check (MANDATORY):**
138873
- 0. Find relevant skills to load - load them IMMEDIATELY.
138874
- 1. Is there a specialized agent that perfectly matches this request?
138875
- 2. If not, what \`task\` category + skills to equip? \u2192 \`task(load_skills=[{skill1}, ...])\`
138876
- 3. Can I do it myself for the best result, FOR SURE?
139048
+ If the user's approach seems problematic, explain your concern and the alternative, then proceed with the better approach. Flag major risks before implementing.
139049
+ </intent>`;
139050
+ const exploreBlock = `<explore>
139051
+ ${toolSelection}
138877
139052
 
138878
- Default bias: DELEGATE for complex tasks. Work yourself ONLY when trivial.
139053
+ ${exploreSection}
138879
139054
 
138880
- ### When to Challenge the User
139055
+ ${librarianSection}
138881
139056
 
138882
- If you observe a design decision that will cause obvious problems, an approach contradicting established patterns, or a request that misunderstands the existing code - note the concern and your alternative clearly, then proceed with the best approach. If the risk is major, flag it before implementing.
139057
+ <tool_usage_rules>
139058
+ - Parallelize independent tool calls: multiple file reads, grep searches, agent fires - all at once
139059
+ - Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel
139060
+ - After any file edit: restate what changed, where, and what validation follows
139061
+ - Prefer tools over guessing whenever you need specific data (files, configs, patterns)
139062
+ </tool_usage_rules>
138883
139063
 
138884
- ---
139064
+ <tool_call_philosophy>
139065
+ More tool calls = more accuracy. Ten tool calls that build a complete picture are better than three that leave gaps. Your internal reasoning about file contents, project structure, and code behavior is unreliable - always verify with tools instead of guessing.
138885
139066
 
138886
- ## Exploration & Research
139067
+ Treat every tool call as an investment in correctness, not a cost to minimize. When you are unsure whether to make a tool call, make it. When you think you have enough context, make one more call to verify. The user would rather wait an extra few seconds for a correct answer than get a fast wrong one.
139068
+ </tool_call_philosophy>
138887
139069
 
138888
- ${toolSelection}
139070
+ <tool_persistence>
139071
+ Do not stop calling tools just to save calls. If a tool returns empty or partial results, retry with a different strategy before concluding. Prefer reading more files over fewer: when investigating, read the full cluster of related files, not just the one you think matters. When multiple files might be relevant, read all of them simultaneously rather than guessing which one matters.
139072
+ </tool_persistence>
138889
139073
 
138890
- ${exploreSection}
139074
+ <dig_deeper>
139075
+ Do not stop at the first plausible answer. Look for second-order issues, edge cases, and missing constraints. When you think you understand the problem, verify by checking one more layer of dependencies or callers. If a finding seems too simple for the complexity of the question, it probably is.
139076
+ </dig_deeper>
138891
139077
 
138892
- ${librarianSection}
139078
+ <dependency_checks>
139079
+ Before taking an action, check whether prerequisite discovery or lookup is required. Do not skip prerequisite steps just because the intended final action seems obvious. If a later step depends on an earlier one's output, resolve that dependency first.
139080
+ </dependency_checks>
138893
139081
 
138894
- ### Parallel Execution & Tool Usage (DEFAULT - NON-NEGOTIABLE)
139082
+ Prefer tools over guessing whenever you need specific data (files, configs, patterns). Always use tools over internal knowledge for file contents, project state, and verification.
138895
139083
 
138896
- Parallelize EVERYTHING. Independent reads, searches, and agents run SIMULTANEOUSLY.
139084
+ <parallel_execution>
139085
+ Parallelize aggressively - this is where you gain the most speed and accuracy. Every independent operation should run simultaneously, not sequentially:
139086
+ - Multiple file reads: read 5 files at once, not one by one
139087
+ - Grep + file reads: search and read in the same turn
139088
+ - Multiple explore/librarian agents: fire 3-5 agents in parallel for different angles on the same question
139089
+ - Agent fires + direct tool calls: launch background agents AND do direct reads simultaneously
138897
139090
 
138898
- <tool_usage_rules>
138899
- - Parallelize independent tool calls: multiple file reads, grep searches, agent fires - all at once.
138900
- - Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel.
138901
- - Never chain together bash commands with separators like \`&&\`, \`;\`, or \`|\` in a single call. Run each command as a separate tool invocation.
138902
- - After any file edit: restate what changed, where, and what validation follows.
138903
- - Prefer tools over guessing whenever you need specific data (files, configs, patterns).
138904
- </tool_usage_rules>
139091
+ Fire 2-5 explore agents in parallel for any non-trivial codebase question. Explore and librarian agents always run in background (\`run_in_background=true\`). Never use \`run_in_background=false\` for explore/librarian. After launching, continue only with non-overlapping work. Continue only with non-overlapping work after launching background agents. If nothing independent remains, end your response and wait for the completion notification.
139092
+ </parallel_execution>
138905
139093
 
138906
- **How to call explore/librarian:**
139094
+ How to call explore/librarian:
138907
139095
  \`\`\`
138908
- // Codebase search - use subagent_type="explore"
139096
+ // Codebase search
138909
139097
  task(subagent_type="explore", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
138910
139098
 
138911
- // External docs/OSS search - use subagent_type="librarian"
139099
+ // External docs/OSS search
138912
139100
  task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
138913
-
138914
139101
  \`\`\`
138915
139102
 
138916
- Prompt structure for each agent:
138917
- - [CONTEXT]: Task, files/modules involved, approach
138918
- - [GOAL]: Specific outcome needed - what decision this unblocks
138919
- - [DOWNSTREAM]: How results will be used
138920
- - [REQUEST]: What to find, format to return, what to SKIP
139103
+ Never chain together bash commands with separators like \`&&\`, \`;\`, or \`|\` in a single call. Run each command as a separate tool invocation.
138921
139104
 
138922
- **Rules:**
138923
- - Fire 2-5 explore agents in parallel for any non-trivial codebase question
138924
- - Parallelize independent file reads - don't read files one at a time
138925
- - NEVER use \`run_in_background=false\` for explore/librarian
138926
- - Continue only with non-overlapping work after launching background agents
138927
- - Collect results with \`background_output(task_id="...")\` when needed
138928
- - BEFORE final answer, cancel DISPOSABLE tasks individually: \`background_cancel(taskId="bg_explore_xxx")\`, \`background_cancel(taskId="bg_librarian_xxx")\`
138929
- - **NEVER use \`background_cancel(all=true)\`** - it kills tasks whose results you haven't collected yet
139105
+ After any file edit, briefly restate what changed, where, and what validation follows.
138930
139106
 
138931
- ${buildAntiDuplicationSection()}
139107
+ Once you delegate exploration to background agents, do not repeat the same search yourself. Continue only with non-overlapping work only. Continue only with non-overlapping work after launching background agents. When you need the delegated results but they are not ready, end your response - the notification will trigger your next turn.
138932
139108
 
138933
- ### Search Stop Conditions
139109
+ Agent prompt structure:
139110
+ - [CONTEXT]: Task, files/modules involved, approach
139111
+ - [GOAL]: Specific outcome needed - what decision this unblocks
139112
+ - [DOWNSTREAM]: How results will be used
139113
+ - [REQUEST]: What to find, format to return, what to skip
138934
139114
 
138935
- STOP searching when you have enough context, the same information keeps appearing, 2 search iterations yielded nothing new, or a direct answer was found. Do not over-explore.
139115
+ Background task management:
139116
+ - Collect results with \`background_output(task_id="...")\` when completed
139117
+ - Before final answer, cancel disposable tasks individually: \`background_cancel(taskId="...")\`
139118
+ - Never use \`background_cancel(all=true)\` - it kills tasks whose results you have not collected yet
138936
139119
 
138937
- ---
139120
+ ${antiDuplication}
138938
139121
 
138939
- ## Execution Loop (EXPLORE \u2192 PLAN \u2192 DECIDE \u2192 EXECUTE \u2192 VERIFY)
139122
+ Stop searching when you have enough context, the same info repeats, or two iterations found nothing new.
139123
+ </explore>`;
139124
+ const constraintsBlock = `<constraints>
139125
+ ${hardBlocks}
138940
139126
 
138941
- 1. **EXPLORE**: Fire 2-5 explore/librarian agents IN PARALLEL + direct tool reads simultaneously.
138942
- 2. **PLAN**: List files to modify, specific changes, dependencies, complexity estimate.
138943
- 3. **DECIDE**: Trivial (<10 lines, single file) \u2192 self. Complex (multi-file, >100 lines) \u2192 MUST delegate.
138944
- 4. **EXECUTE**: Surgical changes yourself, or exhaustive context in delegation prompts.
138945
- 5. **VERIFY**: \`lsp_diagnostics\` on ALL modified files \u2192 build \u2192 tests.
139127
+ ${antiPatterns}
139128
+ </constraints>`;
139129
+ const executionBlock = `<execution>
139130
+ 1. **Explore**: Fire 2-5 explore/librarian agents in parallel + direct tool reads. Goal: complete understanding, not just enough context.
139131
+ 2. **Plan**: List files to modify, specific changes, dependencies, complexity estimate.
139132
+ 3. **Decide**: Trivial (<10 lines, single file) -> self. Complex (multi-file, >100 lines) -> delegate.
139133
+ 4. **Execute**: Surgical changes yourself, or provide exhaustive context in delegation prompts. Match existing patterns. Minimal diff. Search the codebase for similar patterns before writing code. Default to ASCII. Add comments only for non-obvious blocks.
139134
+ 5. **Verify**: \`lsp_diagnostics\` on all modified files (zero errors) -> run related tests (\`foo.ts\` -> \`foo.test.ts\`) -> typecheck -> build if applicable (exit 0). Fix only issues your changes caused.
138946
139135
 
138947
- If verification fails: return to Step 1 (max 3 iterations, then consult Oracle).
139136
+ If verification fails, return to step 1 with a materially different approach. After three attempts: stop, revert to last working state, document what you tried, consult Oracle. If Oracle cannot resolve, ask the user.
138948
139137
 
138949
- ### Scope Discipline
139138
+ While working, you may notice unexpected changes you did not make - likely from the user or autogeneration. If they directly conflict with your task, ask. Otherwise, focus on your task.
138950
139139
 
138951
- While you are working, you might notice unexpected changes that you didn't make. It's likely the user made them, or they were autogenerated. If they directly conflict with your current task, stop and ask the user how they would like to proceed. Otherwise, focus on the task at hand.
139140
+ <completion_check>
139141
+ When you think you are done: re-read the original request. Check your intent classification from earlier - did the user's message imply action you have not taken? Verify every item is fully implemented - not partially, not "extend later." Run verification once more. Then report what you did, what you verified, and the results.
139142
+ </completion_check>
138952
139143
 
138953
- ---
139144
+ <failure_recovery>
139145
+ Fix root causes, not symptoms. Re-verify after every attempt. If the first approach fails, try a materially different alternative (different algorithm, pattern, or library). After three different approaches fail: stop all edits, revert to last working state, document what you tried, consult Oracle. If Oracle cannot resolve, ask the user with a clear explanation.
138954
139146
 
139147
+ Never leave code broken, delete failing tests, or make random changes hoping something works.
139148
+ </failure_recovery>
139149
+ </execution>`;
139150
+ const trackingBlock = `<tracking>
138955
139151
  ${todoDiscipline}
139152
+ </tracking>`;
139153
+ const progressBlock = `<progress>
139154
+ Report progress at meaningful phase transitions. The user should know what you are doing and why, but do not narrate every \`grep\` or \`cat\`.
138956
139155
 
138957
- ---
138958
-
138959
- ## Progress Updates
138960
-
138961
- Report progress proactively every ~30 seconds. The user should always know what you're doing and why.
138962
-
138963
- When to update (MANDATORY):
139156
+ When to update:
138964
139157
  - Before exploration: "Checking the repo structure for auth patterns..."
138965
139158
  - After discovery: "Found the config in \`src/config/\`. The pattern uses factory functions."
138966
139159
  - Before large edits: "About to refactor the handler - touching 3 files."
138967
139160
  - On phase transitions: "Exploration done. Moving to implementation."
138968
139161
  - On blockers: "Hit a snag with the types - trying generics instead."
138969
139162
 
138970
- Style: 1-2 sentences, concrete, with at least one specific detail (file path, pattern found, decision made). When explaining technical decisions, explain the WHY. Don't narrate every \`grep\` or \`cat\`, but DO signal meaningful progress. Keep updates varied in structure - don't start each the same way.
138971
-
138972
- ---
138973
-
138974
- ## Implementation
138975
-
139163
+ Style: one sentence, concrete, with at least one specific detail (file path, pattern found, decision made). Explain the why behind technical decisions. Keep updates varied in structure.
139164
+ </progress>`;
139165
+ const delegationBlock = `<delegation>
138976
139166
  ${categorySkillsGuide}
138977
139167
 
138978
- ### Skill Loading Examples
138979
-
138980
- When delegating, ALWAYS check if relevant skills should be loaded:
138981
-
138982
- - **Frontend/UI work**: \`frontend-ui-ux\` - Anti-slop design: bold typography, intentional color, meaningful motion
138983
- - **Browser testing**: \`playwright\` - Browser automation, screenshots, verification
138984
- - **Git operations**: \`git-master\` - Atomic commits, rebase/squash, blame/bisect
138985
- - **Tauri desktop app**: \`tauri-macos-craft\` - macOS-native UI, vibrancy, traffic lights
138986
-
138987
- User-installed skills get PRIORITY. Always evaluate ALL available skills before delegating.
139168
+ When delegating, check all available skills. User-installed skills get priority. Always evaluate all available skills before delegating. Example domain-skill mappings:
139169
+ - Frontend/UI work: \`frontend-ui-ux\` - Anti-slop design: bold typography, intentional color, meaningful motion
139170
+ - Browser testing: \`playwright\` - Browser automation, screenshots, verification
139171
+ - Git operations: \`git-master\` - Atomic commits, rebase/squash, blame/bisect
139172
+ - Tauri desktop app: \`tauri-macos-craft\` - macOS-native UI, vibrancy, traffic lights
138988
139173
 
138989
139174
  ${delegationTable}
138990
139175
 
138991
- ### Delegation Prompt (MANDATORY 6 sections)
138992
-
138993
- \`\`\`
138994
- 1. TASK: Atomic, specific goal (one action per delegation)
138995
- 2. EXPECTED OUTCOME: Concrete deliverables with success criteria
138996
- 3. REQUIRED TOOLS: Explicit tool whitelist
138997
- 4. MUST DO: Exhaustive requirements - leave NOTHING implicit
138998
- 5. MUST NOT DO: Forbidden actions - anticipate and block rogue behavior
138999
- 6. CONTEXT: File paths, existing patterns, constraints
139000
- \`\`\`
139001
-
139002
- Vague prompts = rejected. Be exhaustive.
139003
-
139004
- After delegation, ALWAYS verify: works as expected? follows codebase pattern? MUST DO / MUST NOT DO respected? NEVER trust subagent self-reports. ALWAYS verify with your own tools.
139176
+ <delegation_prompt>
139177
+ Every delegation prompt needs these 6 sections:
139178
+ 1. TASK: atomic goal
139179
+ 2. EXPECTED OUTCOME: deliverables + success criteria
139180
+ 3. REQUIRED TOOLS: explicit whitelist
139181
+ 4. MUST DO: exhaustive requirements - leave nothing implicit
139182
+ 5. MUST NOT DO: forbidden actions - anticipate rogue behavior
139183
+ 6. CONTEXT: file paths, existing patterns, constraints
139184
+ </delegation_prompt>
139005
139185
 
139006
- ### Session Continuity
139007
-
139008
- Every \`task()\` output includes a session_id. USE IT for follow-ups.
139186
+ After delegation, verify by reading every file the subagent touched. Check: works as expected? follows codebase pattern? Do not trust self-reports.
139009
139187
 
139010
- - Task failed/incomplete - \`session_id="{id}", prompt="Fix: {error}"\`
139011
- - Follow-up on result - \`session_id="{id}", prompt="Also: {question}"\`
139012
- - Verification failed - \`session_id="{id}", prompt="Failed: {error}. Fix."\`
139188
+ <session_continuity>
139189
+ Every \`task()\` returns a session_id. Use it for all follow-ups:
139190
+ - Task failed/incomplete: \`session_id="{id}", prompt="Fix: {error}"\`
139191
+ - Follow-up on result: \`session_id="{id}", prompt="Also: {question}"\`
139192
+ - Verification failed: \`session_id="{id}", prompt="Failed: {error}. Fix."\`
139013
139193
 
139194
+ This preserves full context, avoids repeated exploration, saves 70%+ tokens.
139195
+ </session_continuity>
139014
139196
  ${oracleSection ? `
139015
- ${oracleSection}
139016
- ` : ""}
139017
-
139018
- ## Output Contract
139019
-
139020
- <output_contract>
139021
- Always favor conciseness. Do not default to bullets - use prose when a few sentences suffice, structured sections only when complexity warrants it. Group findings by outcome rather than enumerating every detail.
139022
-
139023
- For simple or single-file tasks, prefer 1-2 short paragraphs. For larger tasks, use at most 2-4 high-level sections. Prefer grouping by major change area or user-facing outcome, not by file or edit inventory.
139024
-
139025
- Do not begin responses with conversational interjections or meta commentary. NEVER open with: "Done -", "Got it", "Great question!", "That's a great idea!", "You're right to call that out".
139026
-
139027
- DO send clear context before significant actions - explain what you're doing and why in plain language so anyone can follow. When explaining technical decisions, explain the WHY, not just the WHAT.
139028
-
139029
- Updates at meaningful milestones must include a concrete outcome ("Found X", "Updated Y"). Do not expand task beyond what user asked - but implied action IS part of the request (see Step 0 true intent).
139030
- </output_contract>
139031
-
139032
- ## Code Quality & Verification
139033
-
139034
- ### Before Writing Code (MANDATORY)
139035
-
139036
- 1. SEARCH existing codebase for similar patterns/styles
139037
- 2. Match naming, indentation, import styles, error handling conventions
139038
- 3. Default to ASCII. Add comments only for non-obvious blocks
139039
-
139040
- ### After Implementation (MANDATORY - DO NOT SKIP)
139041
-
139042
- 1. \`lsp_diagnostics\` on ALL modified files - zero errors required
139043
- 2. Run related tests - pattern: modified \`foo.ts\` \u2192 look for \`foo.test.ts\`
139044
- 3. Run typecheck if TypeScript project
139045
- 4. Run build if applicable - exit code 0 required
139046
- 5. Tell user what you verified and the results
139197
+ ${oracleSection}` : ""}
139198
+ </delegation>`;
139199
+ const communicationBlock = `<communication>
139200
+ Your output is the one part the user actually sees. Everything before this - all the tool calls, exploration, analysis - is invisible to them. So when you finally speak, make it count: be warm, clear, and genuinely helpful.
139047
139201
 
139048
- **NO EVIDENCE = NOT COMPLETE.**
139202
+ Write in complete, natural sentences that anyone can follow. Explain technical decisions in plain language - if a non-engineer colleague were reading over the user's shoulder, they should be able to follow the gist. Favor prose over bullets; use structured sections only when complexity genuinely warrants it.
139049
139203
 
139050
- ## Completion Guarantee (NON-NEGOTIABLE - READ THIS LAST, REMEMBER IT ALWAYS)
139204
+ For simple tasks, 1-2 short paragraphs. For larger tasks, at most 2-4 sections grouped by outcome, not by file. Group findings by outcome rather than enumerating every detail.
139051
139205
 
139052
- You do NOT end your turn until the user's request is 100% done, verified, and proven. Implement everything asked for - no partial delivery, no "basic version". Verify with real tools, not "it should work". Confirm every verification passed. Re-read the original request - did you miss anything? Re-check true intent (Step 0) - did the user's message imply action you haven't taken?
139206
+ When explaining what you did: lead with the result ("Fixed the auth bug - the token was expiring before the refresh check"), then add supporting detail only if it helps understanding. Include concrete details: file paths, patterns found, decisions made. Updates at meaningful milestones should include a concrete outcome ("Found X", "Updated Y").
139053
139207
 
139054
- <turn_end_self_check>
139055
- Before ending your turn, verify ALL of the following:
139208
+ Do not pad responses with conversational openers ("Done -", "Got it", "Great question!"), meta commentary, or acknowledgements. Do not repeat the user's request back. Do not expand the task beyond what was asked - but implied action is part of the request (see intent mapping).
139209
+ </communication>`;
139210
+ return `${identityBlock}
139056
139211
 
139057
- 1. Did the user's message imply action? (Step 0) \u2192 Did you take that action?
139058
- 2. Did you write "I'll do X" or "I recommend X"? \u2192 Did you then DO X?
139059
- 3. Did you offer to do something ("Would you like me to...?") \u2192 VIOLATION. Go back and do it.
139060
- 4. Did you answer a question and stop? \u2192 Was there implied work? If yes, do it now.
139212
+ ${intentBlock}
139061
139213
 
139062
- If ANY check fails: DO NOT end your turn. Continue working.
139063
- </turn_end_self_check>
139214
+ ${exploreBlock}
139064
139215
 
139065
- If ANY of these are false, you are NOT done: all requested functionality fully implemented, \`lsp_diagnostics\` returns zero errors on ALL modified files, build passes (if applicable), tests pass (or pre-existing failures documented), you have EVIDENCE for each verification step.
139216
+ ${constraintsBlock}
139066
139217
 
139067
- Keep going until the task is fully resolved. Persist even when tool calls fail. Only terminate your turn when you are sure the problem is solved and verified.
139218
+ ${executionBlock}
139068
139219
 
139069
- When you think you're done: re-read the request. Run verification ONE MORE TIME. Then report.
139220
+ ${trackingBlock}
139070
139221
 
139071
- ## Failure Recovery
139222
+ ${progressBlock}
139072
139223
 
139073
- Fix root causes, not symptoms. Re-verify after EVERY attempt. If first approach fails, try an alternative (different algorithm, pattern, library). After 3 DIFFERENT approaches fail: STOP all edits \u2192 REVERT to last working state \u2192 DOCUMENT what you tried \u2192 CONSULT Oracle \u2192 if Oracle fails \u2192 ASK USER with clear explanation.
139224
+ ${delegationBlock}
139074
139225
 
139075
- Never leave code broken, delete failing tests, or shotgun debug.`;
139226
+ ${communicationBlock}`;
139076
139227
  }
139077
139228
 
139078
139229
  // src/agents/hephaestus/agent.ts
@@ -140388,7 +140539,7 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
140388
140539
  }
140389
140540
  // src/features/claude-code-agent-loader/loader.ts
140390
140541
  import { existsSync as existsSync85, readdirSync as readdirSync23, readFileSync as readFileSync57 } from "fs";
140391
- import { join as join92, basename as basename14 } from "path";
140542
+ import { join as join93, basename as basename14 } from "path";
140392
140543
  function parseToolsConfig2(toolsStr) {
140393
140544
  if (!toolsStr)
140394
140545
  return;
@@ -140410,7 +140561,7 @@ function loadAgentsFromDir(agentsDir, scope) {
140410
140561
  for (const entry of entries) {
140411
140562
  if (!isMarkdownFile(entry))
140412
140563
  continue;
140413
- const agentPath = join92(agentsDir, entry.name);
140564
+ const agentPath = join93(agentsDir, entry.name);
140414
140565
  const agentName = basename14(entry.name, ".md");
140415
140566
  try {
140416
140567
  const content = readFileSync57(agentPath, "utf-8");
@@ -140443,7 +140594,7 @@ function loadAgentsFromDir(agentsDir, scope) {
140443
140594
  return agents;
140444
140595
  }
140445
140596
  function loadUserAgents() {
140446
- const userAgentsDir = join92(getClaudeConfigDir(), "agents");
140597
+ const userAgentsDir = join93(getClaudeConfigDir(), "agents");
140447
140598
  const agents = loadAgentsFromDir(userAgentsDir, "user");
140448
140599
  const result = {};
140449
140600
  for (const agent of agents) {
@@ -140452,7 +140603,7 @@ function loadUserAgents() {
140452
140603
  return result;
140453
140604
  }
140454
140605
  function loadProjectAgents(directory) {
140455
- const projectAgentsDir = join92(directory ?? process.cwd(), ".claude", "agents");
140606
+ const projectAgentsDir = join93(directory ?? process.cwd(), ".claude", "agents");
140456
140607
  const agents = loadAgentsFromDir(projectAgentsDir, "project");
140457
140608
  const result = {};
140458
140609
  for (const agent of agents) {
@@ -142942,7 +143093,7 @@ async function applyAgentConfig(params) {
142942
143093
  }
142943
143094
  // src/features/claude-code-command-loader/loader.ts
142944
143095
  import { promises as fs19 } from "fs";
142945
- import { join as join93, basename as basename15 } from "path";
143096
+ import { join as join94, basename as basename15 } from "path";
142946
143097
  init_logger();
142947
143098
  async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix = "") {
142948
143099
  try {
@@ -142973,7 +143124,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
142973
143124
  if (entry.isDirectory()) {
142974
143125
  if (entry.name.startsWith("."))
142975
143126
  continue;
142976
- const subDirPath = join93(commandsDir, entry.name);
143127
+ const subDirPath = join94(commandsDir, entry.name);
142977
143128
  const subPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
142978
143129
  const subCommands = await loadCommandsFromDir(subDirPath, scope, visited, subPrefix);
142979
143130
  commands3.push(...subCommands);
@@ -142981,7 +143132,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
142981
143132
  }
142982
143133
  if (!isMarkdownFile(entry))
142983
143134
  continue;
142984
- const commandPath = join93(commandsDir, entry.name);
143135
+ const commandPath = join94(commandsDir, entry.name);
142985
143136
  const baseCommandName = basename15(entry.name, ".md");
142986
143137
  const commandName = prefix ? `${prefix}/${baseCommandName}` : baseCommandName;
142987
143138
  try {
@@ -143040,12 +143191,12 @@ function commandsToRecord(commands3) {
143040
143191
  return result;
143041
143192
  }
143042
143193
  async function loadUserCommands() {
143043
- const userCommandsDir = join93(getClaudeConfigDir(), "commands");
143194
+ const userCommandsDir = join94(getClaudeConfigDir(), "commands");
143044
143195
  const commands3 = await loadCommandsFromDir(userCommandsDir, "user");
143045
143196
  return commandsToRecord(commands3);
143046
143197
  }
143047
143198
  async function loadProjectCommands(directory) {
143048
- const projectCommandsDir = join93(directory ?? process.cwd(), ".claude", "commands");
143199
+ const projectCommandsDir = join94(directory ?? process.cwd(), ".claude", "commands");
143049
143200
  const commands3 = await loadCommandsFromDir(projectCommandsDir, "project");
143050
143201
  return commandsToRecord(commands3);
143051
143202
  }
@@ -143452,7 +143603,9 @@ function createConfigHandler(deps) {
143452
143603
  // src/create-managers.ts
143453
143604
  function createManagers(args) {
143454
143605
  const { ctx, pluginConfig, tmuxConfig, modelCacheState, backgroundNotificationHookEnabled } = args;
143455
- markServerRunningInProcess();
143606
+ if (tmuxConfig.enabled) {
143607
+ markServerRunningInProcess();
143608
+ }
143456
143609
  const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
143457
143610
  registerManagerForCleanup({
143458
143611
  shutdown: async () => {
@@ -143652,8 +143805,14 @@ function trimToolsToCap(filteredTools, maxTools) {
143652
143805
  log(`[tool-registry] Trimmed ${removed} tools to satisfy max_tools=${maxTools}. Final plugin tool count=${currentCount}.`);
143653
143806
  }
143654
143807
  function createToolRegistry(args) {
143655
- const { ctx, pluginConfig, managers, skillContext, availableCategories } = args;
143656
- const tmuxIntegrationEnabled = isTmuxIntegrationEnabled(pluginConfig);
143808
+ const {
143809
+ ctx,
143810
+ pluginConfig,
143811
+ managers,
143812
+ skillContext,
143813
+ availableCategories,
143814
+ interactiveBashEnabled = isInteractiveBashEnabled()
143815
+ } = args;
143657
143816
  const backgroundTools = createBackgroundTools(managers.backgroundManager, ctx.client);
143658
143817
  const callOmoAgent = createCallOmoAgent(ctx, managers.backgroundManager, pluginConfig.disabled_agents ?? [], pluginConfig.agents, pluginConfig.categories);
143659
143818
  const isMultimodalLookerEnabled = !(pluginConfig.disabled_agents ?? []).some((agent) => agent.toLowerCase() === "multimodal-looker");
@@ -143730,7 +143889,7 @@ function createToolRegistry(args) {
143730
143889
  task: delegateTask,
143731
143890
  skill_mcp: skillMcpTool,
143732
143891
  skill: skillTool,
143733
- ...tmuxIntegrationEnabled ? { interactive_bash } : {},
143892
+ ...interactiveBashEnabled ? { interactive_bash } : {},
143734
143893
  ...taskToolsRecord,
143735
143894
  ...hashlineToolsRecord
143736
143895
  };
@@ -144023,10 +144182,10 @@ function createChatHeadersHandler(args) {
144023
144182
 
144024
144183
  // src/plugin/ultrawork-db-model-override.ts
144025
144184
  import { Database } from "bun:sqlite";
144026
- import { join as join94 } from "path";
144185
+ import { join as join95 } from "path";
144027
144186
  import { existsSync as existsSync86 } from "fs";
144028
144187
  function getDbPath() {
144029
- return join94(getDataDir(), "opencode", "opencode.db");
144188
+ return join95(getDataDir(), "opencode", "opencode.db");
144030
144189
  }
144031
144190
  var MAX_MICROTASK_RETRIES = 10;
144032
144191
  function tryUpdateMessageModel(db, messageId, targetModel, variant) {
@@ -144581,7 +144740,8 @@ function isCompactionAgent5(agent) {
144581
144740
  return agent.toLowerCase() === "compaction";
144582
144741
  }
144583
144742
  function createEventHandler2(args) {
144584
- const { ctx, firstMessageVariantGate, managers, hooks: hooks2 } = args;
144743
+ const { ctx, pluginConfig, firstMessageVariantGate, managers, hooks: hooks2 } = args;
144744
+ const tmuxIntegrationEnabled = isTmuxIntegrationEnabled(pluginConfig);
144585
144745
  const pluginContext = ctx;
144586
144746
  const isRuntimeFallbackEnabled = hooks2.runtimeFallback !== null && hooks2.runtimeFallback !== undefined && (typeof args.pluginConfig.runtime_fallback === "boolean" ? args.pluginConfig.runtime_fallback : args.pluginConfig.runtime_fallback?.enabled ?? false);
144587
144747
  const isModelFallbackEnabled = hooks2.modelFallback !== null && hooks2.modelFallback !== undefined;
@@ -144724,7 +144884,7 @@ function createEventHandler2(args) {
144724
144884
  }
144725
144885
  const { event } = input;
144726
144886
  const props = event.properties;
144727
- if (TMUX_ACTIVITY_EVENT_TYPES.has(event.type)) {
144887
+ if (tmuxIntegrationEnabled && TMUX_ACTIVITY_EVENT_TYPES.has(event.type)) {
144728
144888
  managers.tmuxSessionManager.onEvent?.(event);
144729
144889
  }
144730
144890
  if (event.type === "session.created") {
@@ -144733,7 +144893,9 @@ function createEventHandler2(args) {
144733
144893
  setMainSession(sessionInfo?.id);
144734
144894
  }
144735
144895
  firstMessageVariantGate.markSessionCreated(sessionInfo);
144736
- await managers.tmuxSessionManager.onSessionCreated(event);
144896
+ if (tmuxIntegrationEnabled) {
144897
+ await managers.tmuxSessionManager.onSessionCreated(event);
144898
+ }
144737
144899
  }
144738
144900
  if (event.type === "session.deleted") {
144739
144901
  const sessionInfo = props?.info;
@@ -144761,9 +144923,11 @@ function createEventHandler2(args) {
144761
144923
  deleteSessionTools(sessionInfo.id);
144762
144924
  await managers.skillMcpManager.disconnectSession(sessionInfo.id);
144763
144925
  await lspManager.cleanupTempDirectoryClients();
144764
- await managers.tmuxSessionManager.onSessionDeleted({
144765
- sessionID: sessionInfo.id
144766
- });
144926
+ if (tmuxIntegrationEnabled) {
144927
+ await managers.tmuxSessionManager.onSessionDeleted({
144928
+ sessionID: sessionInfo.id
144929
+ });
144930
+ }
144767
144931
  }
144768
144932
  }
144769
144933
  if (event.type === "message.removed") {