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/agents/hephaestus/gpt-5-4.d.ts +22 -1
- package/dist/cli/index.js +182 -116
- package/dist/create-runtime-tmux-config.d.ts +1 -0
- package/dist/features/background-agent/spawner.d.ts +3 -0
- package/dist/features/claude-code-plugin-loader/discovery.d.ts +2 -1
- package/dist/features/claude-code-plugin-loader/loader.d.ts +1 -0
- package/dist/features/claude-code-plugin-loader/types.d.ts +10 -0
- package/dist/hooks/auto-update-checker/checker/sync-package-json.d.ts +1 -1
- package/dist/hooks/auto-update-checker/constants.d.ts +6 -5
- package/dist/hooks/todo-continuation-enforcer/session-state.d.ts +4 -3
- package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
- package/dist/index.js +680 -516
- package/dist/plugin/tool-registry.d.ts +1 -0
- package/package.json +12 -12
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,
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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(
|
|
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-
|
|
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
|
|
84397
|
-
var
|
|
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
|
-
|
|
84404
|
-
|
|
84405
|
-
|
|
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
|
-
|
|
84416
|
-
|
|
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 =
|
|
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
|
|
84649
|
-
return {
|
|
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
|
|
84667
|
-
|
|
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
|
-
|
|
84684
|
-
|
|
84685
|
-
|
|
84686
|
-
|
|
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
|
|
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 ??
|
|
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(
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
85354
|
+
import { join as join58 } from "path";
|
|
85252
85355
|
|
|
85253
85356
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
85254
|
-
import { join as
|
|
85255
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
86707
|
+
import { join as join60 } from "path";
|
|
86605
86708
|
|
|
86606
86709
|
// src/hooks/interactive-bash-session/constants.ts
|
|
86607
|
-
import { join as
|
|
86608
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
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
|
|
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
|
-
|
|
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
|
|
87159
|
-
import { dirname as
|
|
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 ?
|
|
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 =
|
|
87313
|
+
const dir = dirname15(filePath);
|
|
87211
87314
|
if (!existsSync55(dir)) {
|
|
87212
|
-
|
|
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
|
|
88442
|
+
import { dirname as dirname18 } from "path";
|
|
88340
88443
|
// src/features/opencode-skill-loader/loader.ts
|
|
88341
|
-
import { join as
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
88595
|
+
const entryPath = join63(options.skillsDir, entry.name);
|
|
88493
88596
|
const resolvedPath = await resolveSymlinkAsync(entryPath);
|
|
88494
88597
|
const dirName = entry.name;
|
|
88495
|
-
const skillMdPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 ?
|
|
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
|
|
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
|
|
92583
|
+
return join65(homedir13(), path11.slice(2));
|
|
92481
92584
|
}
|
|
92482
92585
|
if (isAbsolute8(path11)) {
|
|
92483
92586
|
return path11;
|
|
92484
92587
|
}
|
|
92485
|
-
return
|
|
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:
|
|
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],
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
94169
|
-
const projectCommandsDir =
|
|
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 ?
|
|
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
|
|
94655
|
-
import { dirname as
|
|
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
|
|
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 =
|
|
94788
|
+
const dir = dirname19(filePath);
|
|
94686
94789
|
if (!existsSync59(dir)) {
|
|
94687
|
-
|
|
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 =
|
|
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) =>
|
|
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
|
|
98155
|
+
import { join as join68, dirname as dirname20, basename as basename9 } from "path";
|
|
98053
98156
|
function buildCanonicalPath(legacyPath) {
|
|
98054
|
-
const dir =
|
|
98157
|
+
const dir = dirname20(legacyPath);
|
|
98055
98158
|
const ext = basename9(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
|
|
98056
|
-
return
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 =
|
|
99855
|
+
const absoluteDir = dirname22(absolutePath);
|
|
99747
99856
|
const resolvedDir = existsSync65(absoluteDir) ? realpathSync7.native(absoluteDir) : absoluteDir;
|
|
99748
|
-
canonicalPath =
|
|
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
|
|
100873
|
+
import { join as join71 } from "path";
|
|
100765
100874
|
function detectOpenCodeConfigPath(overrideConfigDir) {
|
|
100766
100875
|
if (overrideConfigDir) {
|
|
100767
|
-
const jsoncPath =
|
|
100768
|
-
const jsonPath =
|
|
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
|
|
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(
|
|
101305
|
+
project: detectPluginConfigFile(join72(cwd, ".opencode")).path,
|
|
101197
101306
|
user: detectPluginConfigFile(configDir).path,
|
|
101198
|
-
opencode: detectConfigFile(
|
|
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
|
|
101377
|
+
import { delimiter, join as join74 } from "path";
|
|
101269
101378
|
|
|
101270
101379
|
// src/tools/lsp/server-path-bases.ts
|
|
101271
|
-
import { join as
|
|
101380
|
+
import { join as join73 } from "path";
|
|
101272
101381
|
function getLspServerAdditionalPathBases(workingDirectory) {
|
|
101273
101382
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
101274
|
-
const dataDir =
|
|
101383
|
+
const dataDir = join73(getDataDir(), "opencode");
|
|
101275
101384
|
return [
|
|
101276
|
-
|
|
101277
|
-
|
|
101278
|
-
|
|
101279
|
-
|
|
101280
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 ||
|
|
115226
|
-
return
|
|
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 ||
|
|
115230
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
115310
|
-
const sgPath =
|
|
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 =
|
|
115429
|
+
const packageDirectory = dirname23(packageJsonPath);
|
|
115321
115430
|
const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
115322
|
-
const binaryPath =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
115865
|
+
return join79(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
115757
115866
|
}
|
|
115758
115867
|
function getRgPath() {
|
|
115759
115868
|
const isWindows2 = process.platform === "win32";
|
|
115760
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
115845
|
-
|
|
115846
|
-
|
|
115847
|
-
|
|
115848
|
-
|
|
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
|
|
116608
|
+
import { dirname as dirname26 } from "path";
|
|
116500
116609
|
// src/tools/slashcommand/command-output-formatter.ts
|
|
116501
|
-
import { dirname as
|
|
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 ?
|
|
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 ?
|
|
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
|
|
116961
|
-
var TODO_DIR2 =
|
|
116962
|
-
var TRANSCRIPT_DIR2 =
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
119441
|
-
const outputPath =
|
|
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 =
|
|
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(
|
|
119679
|
+
const tempDir = mkdtempSync(join83(tmpdir7(), "opencode-b64-"));
|
|
119507
119680
|
const inputExt = mimeType.split("/")[1] || "bin";
|
|
119508
|
-
const inputPath =
|
|
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
|
|
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
|
|
121847
|
-
import { existsSync as existsSync80, mkdirSync as
|
|
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 :
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
127129
|
-
import { dirname as
|
|
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
|
|
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 =
|
|
127407
|
+
const dir = dirname29(filePath);
|
|
127183
127408
|
if (!existsSync82(dir)) {
|
|
127184
|
-
|
|
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
|
|
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:
|
|
133687
|
-
{ path:
|
|
133688
|
-
{ path:
|
|
133689
|
-
{ path:
|
|
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 -
|
|
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
|
|
138730
|
-
2. Before each step
|
|
138731
|
-
3. After each step
|
|
138732
|
-
4. Scope changes
|
|
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 -
|
|
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
|
|
138751
|
-
2. Before each step
|
|
138752
|
-
3. After each step
|
|
138753
|
-
4. Scope changes
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) |
|
|
138827
|
-
| "How does X work?" | Understand
|
|
138828
|
-
| "Can you look into Y?" | Investigate
|
|
138829
|
-
| "What's the best way to do Z?" |
|
|
138830
|
-
| "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose
|
|
138831
|
-
| "What do you think about C?" | Evaluate
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138860
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
138873
|
-
|
|
138874
|
-
|
|
138875
|
-
|
|
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
|
-
|
|
139053
|
+
${exploreSection}
|
|
138879
139054
|
|
|
138880
|
-
|
|
139055
|
+
${librarianSection}
|
|
138881
139056
|
|
|
138882
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138899
|
-
|
|
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
|
-
|
|
139094
|
+
How to call explore/librarian:
|
|
138907
139095
|
\`\`\`
|
|
138908
|
-
// Codebase search
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138942
|
-
|
|
138943
|
-
|
|
138944
|
-
|
|
138945
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
138979
|
-
|
|
138980
|
-
|
|
138981
|
-
|
|
138982
|
-
-
|
|
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
|
-
|
|
138992
|
-
|
|
138993
|
-
|
|
138994
|
-
|
|
138995
|
-
|
|
138996
|
-
|
|
138997
|
-
|
|
138998
|
-
|
|
138999
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139011
|
-
|
|
139012
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139055
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139063
|
-
</turn_end_self_check>
|
|
139214
|
+
${exploreBlock}
|
|
139064
139215
|
|
|
139065
|
-
|
|
139216
|
+
${constraintsBlock}
|
|
139066
139217
|
|
|
139067
|
-
|
|
139218
|
+
${executionBlock}
|
|
139068
139219
|
|
|
139069
|
-
|
|
139220
|
+
${trackingBlock}
|
|
139070
139221
|
|
|
139071
|
-
|
|
139222
|
+
${progressBlock}
|
|
139072
139223
|
|
|
139073
|
-
|
|
139224
|
+
${delegationBlock}
|
|
139074
139225
|
|
|
139075
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
143656
|
-
|
|
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
|
-
...
|
|
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
|
|
144185
|
+
import { join as join95 } from "path";
|
|
144027
144186
|
import { existsSync as existsSync86 } from "fs";
|
|
144028
144187
|
function getDbPath() {
|
|
144029
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
144765
|
-
|
|
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") {
|