evil-omo 3.11.7 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/config-manager/bun-install.d.ts +2 -0
- package/dist/cli/index.js +142 -105
- package/dist/config/schema/background-task.d.ts +6 -0
- package/dist/config/schema/evil-omo-config.d.ts +6 -0
- package/dist/evil-omo.schema.json +26 -0
- package/dist/features/background-agent/constants.d.ts +5 -1
- package/dist/features/background-agent/loop-detector.d.ts +17 -0
- package/dist/features/background-agent/types.d.ts +7 -0
- package/dist/hooks/ralph-loop/pending-verification-handler.d.ts +1 -0
- package/dist/hooks/todo-continuation-enforcer/session-state.d.ts +1 -1
- package/dist/index.js +642 -337
- package/dist/shared/plugin-identity.d.ts +2 -1
- package/dist/shared/shell-env.d.ts +27 -0
- package/dist/tools/delegate-task/model-selection.d.ts +2 -0
- package/dist/tools/hashline-edit/tool-description.d.ts +1 -1
- package/dist/tools/look-at/constants.d.ts +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -982,10 +982,10 @@ function detectManagedConfigFile(directory) {
|
|
|
982
982
|
baseName: CONFIG_BASENAME
|
|
983
983
|
};
|
|
984
984
|
}
|
|
985
|
-
var PLUGIN_NAME = "evil-omo", ALL_PLUGIN_NAMES, CONFIG_BASENAME = "evil-omo", ALL_CONFIG_BASENAMES, LOG_FILENAME = "evil-omo.log", CACHE_DIR_NAME = "evil-omo", SCHEMA_FILENAME = "evil-omo.schema.json";
|
|
985
|
+
var PLUGIN_NAME = "evil-omo", LEGACY_PLUGIN_NAME = "oh-my-opencode", ALL_PLUGIN_NAMES, CONFIG_BASENAME = "evil-omo", ALL_CONFIG_BASENAMES, LOG_FILENAME = "evil-omo.log", CACHE_DIR_NAME = "evil-omo", SCHEMA_FILENAME = "evil-omo.schema.json";
|
|
986
986
|
var init_plugin_identity = __esm(() => {
|
|
987
987
|
init_jsonc_parser();
|
|
988
|
-
ALL_PLUGIN_NAMES = [PLUGIN_NAME];
|
|
988
|
+
ALL_PLUGIN_NAMES = [PLUGIN_NAME, LEGACY_PLUGIN_NAME];
|
|
989
989
|
ALL_CONFIG_BASENAMES = [CONFIG_BASENAME];
|
|
990
990
|
});
|
|
991
991
|
|
|
@@ -17575,6 +17575,9 @@ function buildEnvPrefix(env, shellType) {
|
|
|
17575
17575
|
return "";
|
|
17576
17576
|
}
|
|
17577
17577
|
}
|
|
17578
|
+
function shellEscapeForDoubleQuotedCommand(value) {
|
|
17579
|
+
return value.replace(/\\/g, "\\\\").replace(/\$/g, "\\$").replace(/`/g, "\\`").replace(/"/g, "\\\"").replace(/;/g, "\\;").replace(/\|/g, "\\|").replace(/&/g, "\\&").replace(/#/g, "\\#").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
17580
|
+
}
|
|
17578
17581
|
// src/shared/system-directive.ts
|
|
17579
17582
|
var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: OMO-PLUGIN";
|
|
17580
17583
|
function createSystemDirective(type2) {
|
|
@@ -18593,7 +18596,8 @@ async function spawnTmuxPane(sessionId, description, config, serverUrl, targetPa
|
|
|
18593
18596
|
}
|
|
18594
18597
|
log2("[spawnTmuxPane] all checks passed, spawning...");
|
|
18595
18598
|
const shell = process.env.SHELL || "/bin/sh";
|
|
18596
|
-
const
|
|
18599
|
+
const escapedUrl = shellEscapeForDoubleQuotedCommand(serverUrl);
|
|
18600
|
+
const opencodeCmd = `${shell} -c "opencode attach ${escapedUrl} --session ${sessionId}"`;
|
|
18597
18601
|
const args = [
|
|
18598
18602
|
"split-window",
|
|
18599
18603
|
splitDirection,
|
|
@@ -18688,7 +18692,8 @@ async function replaceTmuxPane(paneId, sessionId, description, config, serverUrl
|
|
|
18688
18692
|
});
|
|
18689
18693
|
await ctrlCProc.exited;
|
|
18690
18694
|
const shell = process.env.SHELL || "/bin/sh";
|
|
18691
|
-
const
|
|
18695
|
+
const escapedUrl = shellEscapeForDoubleQuotedCommand(serverUrl);
|
|
18696
|
+
const opencodeCmd = `${shell} -c "opencode attach ${escapedUrl} --session ${sessionId}"`;
|
|
18692
18697
|
const proc = spawn7([tmux, "respawn-pane", "-k", "-t", paneId, opencodeCmd], {
|
|
18693
18698
|
stdout: "pipe",
|
|
18694
18699
|
stderr: "pipe"
|
|
@@ -18710,6 +18715,7 @@ async function replaceTmuxPane(paneId, sessionId, description, config, serverUrl
|
|
|
18710
18715
|
const titleStderr = await stderrPromise;
|
|
18711
18716
|
log2("[replaceTmuxPane] WARNING: failed to set pane title", {
|
|
18712
18717
|
paneId,
|
|
18718
|
+
title,
|
|
18713
18719
|
exitCode: titleExitCode,
|
|
18714
18720
|
stderr: titleStderr.trim()
|
|
18715
18721
|
});
|
|
@@ -20754,17 +20760,8 @@ function createSessionStateStore() {
|
|
|
20754
20760
|
};
|
|
20755
20761
|
const trackedSession = {
|
|
20756
20762
|
state: rawState,
|
|
20757
|
-
lastAccessedAt: Date.now()
|
|
20758
|
-
activitySignalCount: 0
|
|
20763
|
+
lastAccessedAt: Date.now()
|
|
20759
20764
|
};
|
|
20760
|
-
trackedSession.state = new Proxy(rawState, {
|
|
20761
|
-
set(target, property, value, receiver) {
|
|
20762
|
-
if (property === "abortDetectedAt" && value === undefined) {
|
|
20763
|
-
trackedSession.activitySignalCount += 1;
|
|
20764
|
-
}
|
|
20765
|
-
return Reflect.set(target, property, value, receiver);
|
|
20766
|
-
}
|
|
20767
|
-
});
|
|
20768
20765
|
sessions.set(sessionID, trackedSession);
|
|
20769
20766
|
return trackedSession;
|
|
20770
20767
|
}
|
|
@@ -20786,10 +20783,8 @@ function createSessionStateStore() {
|
|
|
20786
20783
|
const previousStagnationCount = state2.stagnationCount;
|
|
20787
20784
|
const currentCompletedCount = todos?.filter((todo) => todo.status === "completed").length;
|
|
20788
20785
|
const currentTodoSnapshot = todos ? getTodoSnapshot(todos) : undefined;
|
|
20789
|
-
const currentActivitySignalCount = trackedSession.activitySignalCount;
|
|
20790
20786
|
const hasCompletedMoreTodos = currentCompletedCount !== undefined && trackedSession.lastCompletedCount !== undefined && currentCompletedCount > trackedSession.lastCompletedCount;
|
|
20791
20787
|
const hasTodoSnapshotChanged = currentTodoSnapshot !== undefined && trackedSession.lastTodoSnapshot !== undefined && currentTodoSnapshot !== trackedSession.lastTodoSnapshot;
|
|
20792
|
-
const hasObservedExternalActivity = trackedSession.lastObservedActivitySignalCount !== undefined && currentActivitySignalCount > trackedSession.lastObservedActivitySignalCount;
|
|
20793
20788
|
const hadSuccessfulInjectionAwaitingProgressCheck = state2.awaitingPostInjectionProgressCheck === true;
|
|
20794
20789
|
state2.lastIncompleteCount = incompleteCount;
|
|
20795
20790
|
if (currentCompletedCount !== undefined) {
|
|
@@ -20798,7 +20793,6 @@ function createSessionStateStore() {
|
|
|
20798
20793
|
if (currentTodoSnapshot !== undefined) {
|
|
20799
20794
|
trackedSession.lastTodoSnapshot = currentTodoSnapshot;
|
|
20800
20795
|
}
|
|
20801
|
-
trackedSession.lastObservedActivitySignalCount = currentActivitySignalCount;
|
|
20802
20796
|
if (previousIncompleteCount === undefined) {
|
|
20803
20797
|
state2.stagnationCount = 0;
|
|
20804
20798
|
return {
|
|
@@ -20809,7 +20803,7 @@ function createSessionStateStore() {
|
|
|
20809
20803
|
progressSource: "none"
|
|
20810
20804
|
};
|
|
20811
20805
|
}
|
|
20812
|
-
const progressSource = incompleteCount < previousIncompleteCount || hasCompletedMoreTodos || hasTodoSnapshotChanged ? "todo" :
|
|
20806
|
+
const progressSource = incompleteCount < previousIncompleteCount || hasCompletedMoreTodos || hasTodoSnapshotChanged ? "todo" : "none";
|
|
20813
20807
|
if (progressSource !== "none") {
|
|
20814
20808
|
state2.stagnationCount = 0;
|
|
20815
20809
|
state2.awaitingPostInjectionProgressCheck = false;
|
|
@@ -20851,8 +20845,6 @@ function createSessionStateStore() {
|
|
|
20851
20845
|
state2.awaitingPostInjectionProgressCheck = false;
|
|
20852
20846
|
trackedSession.lastCompletedCount = undefined;
|
|
20853
20847
|
trackedSession.lastTodoSnapshot = undefined;
|
|
20854
|
-
trackedSession.activitySignalCount = 0;
|
|
20855
|
-
trackedSession.lastObservedActivitySignalCount = undefined;
|
|
20856
20848
|
}
|
|
20857
20849
|
function cancelCountdown(sessionID) {
|
|
20858
20850
|
const tracked = sessions.get(sessionID);
|
|
@@ -38723,6 +38715,16 @@ class TaskToastManager {
|
|
|
38723
38715
|
const running = this.getRunningTasks();
|
|
38724
38716
|
const queued = this.getQueuedTasks();
|
|
38725
38717
|
const concurrencyInfo = this.getConcurrencyInfo();
|
|
38718
|
+
const formatTaskIdentifier = (task) => {
|
|
38719
|
+
const modelName = task.modelInfo?.model?.split("/").pop();
|
|
38720
|
+
if (modelName && task.category)
|
|
38721
|
+
return `${modelName}: ${task.category}`;
|
|
38722
|
+
if (modelName)
|
|
38723
|
+
return modelName;
|
|
38724
|
+
if (task.category)
|
|
38725
|
+
return `${task.agent}/${task.category}`;
|
|
38726
|
+
return task.agent;
|
|
38727
|
+
};
|
|
38726
38728
|
const lines = [];
|
|
38727
38729
|
const isFallback = newTask.modelInfo && (newTask.modelInfo.type === "inherited" || newTask.modelInfo.type === "system-default" || newTask.modelInfo.type === "runtime-fallback");
|
|
38728
38730
|
if (isFallback) {
|
|
@@ -38741,9 +38743,9 @@ class TaskToastManager {
|
|
|
38741
38743
|
const duration3 = this.formatDuration(task.startedAt);
|
|
38742
38744
|
const bgIcon = task.isBackground ? "[BG]" : "[RUN]";
|
|
38743
38745
|
const isNew = task.id === newTask.id ? " \u2190 NEW" : "";
|
|
38744
|
-
const
|
|
38746
|
+
const taskId = formatTaskIdentifier(task);
|
|
38745
38747
|
const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : "";
|
|
38746
|
-
lines.push(`${bgIcon} ${task.description} (${
|
|
38748
|
+
lines.push(`${bgIcon} ${task.description} (${taskId})${skillsInfo} - ${duration3}${isNew}`);
|
|
38747
38749
|
}
|
|
38748
38750
|
}
|
|
38749
38751
|
if (queued.length > 0) {
|
|
@@ -38752,10 +38754,10 @@ class TaskToastManager {
|
|
|
38752
38754
|
lines.push(`Queued (${queued.length}):`);
|
|
38753
38755
|
for (const task of queued) {
|
|
38754
38756
|
const bgIcon = task.isBackground ? "[Q]" : "[W]";
|
|
38755
|
-
const
|
|
38757
|
+
const taskId = formatTaskIdentifier(task);
|
|
38756
38758
|
const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : "";
|
|
38757
38759
|
const isNew = task.id === newTask.id ? " \u2190 NEW" : "";
|
|
38758
|
-
lines.push(`${bgIcon} ${task.description} (${
|
|
38760
|
+
lines.push(`${bgIcon} ${task.description} (${taskId})${skillsInfo} - Queued${isNew}`);
|
|
38759
38761
|
}
|
|
38760
38762
|
}
|
|
38761
38763
|
return lines.join(`
|
|
@@ -41073,6 +41075,9 @@ function syncCachePackageJsonToIntent(pluginInfo) {
|
|
|
41073
41075
|
return { synced: false, error: "write_error", message: "Failed to write cache package.json" };
|
|
41074
41076
|
}
|
|
41075
41077
|
}
|
|
41078
|
+
// src/hooks/auto-update-checker/hook/background-update-check.ts
|
|
41079
|
+
import { existsSync as existsSync48 } from "fs";
|
|
41080
|
+
import { join as join54 } from "path";
|
|
41076
41081
|
// src/cli/config-manager/plugin-name-with-version.ts
|
|
41077
41082
|
init_plugin_identity();
|
|
41078
41083
|
// src/cli/config-manager/add-plugin-to-opencode-config.ts
|
|
@@ -41167,7 +41172,7 @@ function logCapturedOutputOnFailure(outputMode, output) {
|
|
|
41167
41172
|
}
|
|
41168
41173
|
async function runBunInstallWithDetails(options) {
|
|
41169
41174
|
const outputMode = options?.outputMode ?? "pipe";
|
|
41170
|
-
const cacheDir = getOpenCodeCacheDir();
|
|
41175
|
+
const cacheDir = options?.workspaceDir ?? getOpenCodeCacheDir();
|
|
41171
41176
|
const packageJsonPath = `${cacheDir}/package.json`;
|
|
41172
41177
|
if (!existsSync46(packageJsonPath)) {
|
|
41173
41178
|
return {
|
|
@@ -41329,9 +41334,25 @@ Restart OpenCode to apply.`,
|
|
|
41329
41334
|
function getPinnedVersionToastMessage(latestVersion) {
|
|
41330
41335
|
return `Update available: ${latestVersion} (version pinned, update manually)`;
|
|
41331
41336
|
}
|
|
41332
|
-
|
|
41337
|
+
function resolveActiveInstallWorkspace() {
|
|
41338
|
+
const configPaths = getOpenCodeConfigPaths({ binary: "opencode" });
|
|
41339
|
+
const cacheDir = getOpenCodeCacheDir();
|
|
41340
|
+
const configInstallPath = join54(configPaths.configDir, "node_modules", PACKAGE_NAME, "package.json");
|
|
41341
|
+
const cacheInstallPath = join54(cacheDir, "node_modules", PACKAGE_NAME, "package.json");
|
|
41342
|
+
if (existsSync48(configInstallPath)) {
|
|
41343
|
+
log(`[auto-update-checker] Active workspace: config-dir (${configPaths.configDir})`);
|
|
41344
|
+
return configPaths.configDir;
|
|
41345
|
+
}
|
|
41346
|
+
if (existsSync48(cacheInstallPath)) {
|
|
41347
|
+
log(`[auto-update-checker] Active workspace: cache-dir (${cacheDir})`);
|
|
41348
|
+
return cacheDir;
|
|
41349
|
+
}
|
|
41350
|
+
log(`[auto-update-checker] Active workspace: config-dir (default, no install detected)`);
|
|
41351
|
+
return configPaths.configDir;
|
|
41352
|
+
}
|
|
41353
|
+
async function runBunInstallSafe(workspaceDir) {
|
|
41333
41354
|
try {
|
|
41334
|
-
const result = await runBunInstallWithDetails({ outputMode: "pipe" });
|
|
41355
|
+
const result = await runBunInstallWithDetails({ outputMode: "pipe", workspaceDir });
|
|
41335
41356
|
if (!result.success && result.error) {
|
|
41336
41357
|
log("[auto-update-checker] bun install error:", result.error);
|
|
41337
41358
|
}
|
|
@@ -41382,7 +41403,8 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage) {
|
|
|
41382
41403
|
return;
|
|
41383
41404
|
}
|
|
41384
41405
|
invalidatePackage(PACKAGE_NAME);
|
|
41385
|
-
const
|
|
41406
|
+
const activeWorkspace = resolveActiveInstallWorkspace();
|
|
41407
|
+
const installSuccess = await runBunInstallSafe(activeWorkspace);
|
|
41386
41408
|
if (installSuccess) {
|
|
41387
41409
|
await showAutoUpdatedToast(ctx, currentVersion, latestVersion);
|
|
41388
41410
|
log(`[auto-update-checker] Update installed: ${currentVersion} \u2192 ${latestVersion}`);
|
|
@@ -41557,17 +41579,17 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
|
|
|
41557
41579
|
}
|
|
41558
41580
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
41559
41581
|
import {
|
|
41560
|
-
existsSync as
|
|
41582
|
+
existsSync as existsSync49,
|
|
41561
41583
|
mkdirSync as mkdirSync10,
|
|
41562
41584
|
readFileSync as readFileSync33,
|
|
41563
41585
|
writeFileSync as writeFileSync14,
|
|
41564
41586
|
unlinkSync as unlinkSync8
|
|
41565
41587
|
} from "fs";
|
|
41566
|
-
import { join as
|
|
41588
|
+
import { join as join56 } from "path";
|
|
41567
41589
|
|
|
41568
41590
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
41569
|
-
import { join as
|
|
41570
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
41591
|
+
import { join as join55 } from "path";
|
|
41592
|
+
var AGENT_USAGE_REMINDER_STORAGE = join55(OPENCODE_STORAGE, "agent-usage-reminder");
|
|
41571
41593
|
var TARGET_TOOLS = new Set([
|
|
41572
41594
|
"grep",
|
|
41573
41595
|
"safe_grep",
|
|
@@ -41613,11 +41635,11 @@ ALWAYS prefer: Multiple parallel task calls > Direct tool calls
|
|
|
41613
41635
|
|
|
41614
41636
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
41615
41637
|
function getStoragePath2(sessionID) {
|
|
41616
|
-
return
|
|
41638
|
+
return join56(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
41617
41639
|
}
|
|
41618
41640
|
function loadAgentUsageState(sessionID) {
|
|
41619
41641
|
const filePath = getStoragePath2(sessionID);
|
|
41620
|
-
if (!
|
|
41642
|
+
if (!existsSync49(filePath))
|
|
41621
41643
|
return null;
|
|
41622
41644
|
try {
|
|
41623
41645
|
const content = readFileSync33(filePath, "utf-8");
|
|
@@ -41627,7 +41649,7 @@ function loadAgentUsageState(sessionID) {
|
|
|
41627
41649
|
}
|
|
41628
41650
|
}
|
|
41629
41651
|
function saveAgentUsageState(state3) {
|
|
41630
|
-
if (!
|
|
41652
|
+
if (!existsSync49(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
41631
41653
|
mkdirSync10(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
41632
41654
|
}
|
|
41633
41655
|
const filePath = getStoragePath2(state3.sessionID);
|
|
@@ -41635,7 +41657,7 @@ function saveAgentUsageState(state3) {
|
|
|
41635
41657
|
}
|
|
41636
41658
|
function clearAgentUsageState(sessionID) {
|
|
41637
41659
|
const filePath = getStoragePath2(sessionID);
|
|
41638
|
-
if (
|
|
41660
|
+
if (existsSync49(filePath)) {
|
|
41639
41661
|
unlinkSync8(filePath);
|
|
41640
41662
|
}
|
|
41641
41663
|
}
|
|
@@ -42899,17 +42921,17 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
42899
42921
|
}
|
|
42900
42922
|
// src/hooks/interactive-bash-session/storage.ts
|
|
42901
42923
|
import {
|
|
42902
|
-
existsSync as
|
|
42924
|
+
existsSync as existsSync50,
|
|
42903
42925
|
mkdirSync as mkdirSync11,
|
|
42904
42926
|
readFileSync as readFileSync34,
|
|
42905
42927
|
writeFileSync as writeFileSync15,
|
|
42906
42928
|
unlinkSync as unlinkSync9
|
|
42907
42929
|
} from "fs";
|
|
42908
|
-
import { join as
|
|
42930
|
+
import { join as join58 } from "path";
|
|
42909
42931
|
|
|
42910
42932
|
// src/hooks/interactive-bash-session/constants.ts
|
|
42911
|
-
import { join as
|
|
42912
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
42933
|
+
import { join as join57 } from "path";
|
|
42934
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join57(OPENCODE_STORAGE, "interactive-bash-session");
|
|
42913
42935
|
var OMO_SESSION_PREFIX = "omo-";
|
|
42914
42936
|
function buildSessionReminderMessage(sessions) {
|
|
42915
42937
|
if (sessions.length === 0)
|
|
@@ -42921,11 +42943,11 @@ function buildSessionReminderMessage(sessions) {
|
|
|
42921
42943
|
|
|
42922
42944
|
// src/hooks/interactive-bash-session/storage.ts
|
|
42923
42945
|
function getStoragePath3(sessionID) {
|
|
42924
|
-
return
|
|
42946
|
+
return join58(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
42925
42947
|
}
|
|
42926
42948
|
function loadInteractiveBashSessionState(sessionID) {
|
|
42927
42949
|
const filePath = getStoragePath3(sessionID);
|
|
42928
|
-
if (!
|
|
42950
|
+
if (!existsSync50(filePath))
|
|
42929
42951
|
return null;
|
|
42930
42952
|
try {
|
|
42931
42953
|
const content = readFileSync34(filePath, "utf-8");
|
|
@@ -42940,7 +42962,7 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
42940
42962
|
}
|
|
42941
42963
|
}
|
|
42942
42964
|
function saveInteractiveBashSessionState(state3) {
|
|
42943
|
-
if (!
|
|
42965
|
+
if (!existsSync50(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
42944
42966
|
mkdirSync11(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
42945
42967
|
}
|
|
42946
42968
|
const filePath = getStoragePath3(state3.sessionID);
|
|
@@ -42953,7 +42975,7 @@ function saveInteractiveBashSessionState(state3) {
|
|
|
42953
42975
|
}
|
|
42954
42976
|
function clearInteractiveBashSessionState(sessionID) {
|
|
42955
42977
|
const filePath = getStoragePath3(sessionID);
|
|
42956
|
-
if (
|
|
42978
|
+
if (existsSync50(filePath)) {
|
|
42957
42979
|
unlinkSync9(filePath);
|
|
42958
42980
|
}
|
|
42959
42981
|
}
|
|
@@ -43350,14 +43372,14 @@ var DEFAULT_MAX_ITERATIONS = 100;
|
|
|
43350
43372
|
var DEFAULT_COMPLETION_PROMISE = "DONE";
|
|
43351
43373
|
var ULTRAWORK_VERIFICATION_PROMISE = "VERIFIED";
|
|
43352
43374
|
// src/hooks/ralph-loop/storage.ts
|
|
43353
|
-
import { existsSync as
|
|
43354
|
-
import { dirname as dirname11, join as
|
|
43375
|
+
import { existsSync as existsSync51, readFileSync as readFileSync35, writeFileSync as writeFileSync16, unlinkSync as unlinkSync10, mkdirSync as mkdirSync12 } from "fs";
|
|
43376
|
+
import { dirname as dirname11, join as join59 } from "path";
|
|
43355
43377
|
function getStateFilePath(directory, customPath) {
|
|
43356
|
-
return customPath ?
|
|
43378
|
+
return customPath ? join59(directory, customPath) : join59(directory, DEFAULT_STATE_FILE);
|
|
43357
43379
|
}
|
|
43358
43380
|
function readState(directory, customPath) {
|
|
43359
43381
|
const filePath = getStateFilePath(directory, customPath);
|
|
43360
|
-
if (!
|
|
43382
|
+
if (!existsSync51(filePath)) {
|
|
43361
43383
|
return null;
|
|
43362
43384
|
}
|
|
43363
43385
|
try {
|
|
@@ -43403,7 +43425,7 @@ function writeState(directory, state3, customPath) {
|
|
|
43403
43425
|
const filePath = getStateFilePath(directory, customPath);
|
|
43404
43426
|
try {
|
|
43405
43427
|
const dir = dirname11(filePath);
|
|
43406
|
-
if (!
|
|
43428
|
+
if (!existsSync51(dir)) {
|
|
43407
43429
|
mkdirSync12(dir, { recursive: true });
|
|
43408
43430
|
}
|
|
43409
43431
|
const sessionIdLine = state3.session_id ? `session_id: "${state3.session_id}"
|
|
@@ -43441,7 +43463,7 @@ ${state3.prompt}
|
|
|
43441
43463
|
function clearState(directory, customPath) {
|
|
43442
43464
|
const filePath = getStateFilePath(directory, customPath);
|
|
43443
43465
|
try {
|
|
43444
|
-
if (
|
|
43466
|
+
if (existsSync51(filePath)) {
|
|
43445
43467
|
unlinkSync10(filePath);
|
|
43446
43468
|
}
|
|
43447
43469
|
return true;
|
|
@@ -43770,7 +43792,7 @@ async function handleDetectedCompletion(ctx, input) {
|
|
|
43770
43792
|
|
|
43771
43793
|
// src/hooks/ralph-loop/completion-promise-detector.ts
|
|
43772
43794
|
init_logger();
|
|
43773
|
-
import { existsSync as
|
|
43795
|
+
import { existsSync as existsSync52, readFileSync as readFileSync36 } from "fs";
|
|
43774
43796
|
function escapeRegex2(str2) {
|
|
43775
43797
|
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43776
43798
|
}
|
|
@@ -43781,7 +43803,7 @@ function detectCompletionInTranscript(transcriptPath, promise2, startedAt) {
|
|
|
43781
43803
|
if (!transcriptPath)
|
|
43782
43804
|
return false;
|
|
43783
43805
|
try {
|
|
43784
|
-
if (!
|
|
43806
|
+
if (!existsSync52(transcriptPath))
|
|
43785
43807
|
return false;
|
|
43786
43808
|
const content = readFileSync36(transcriptPath, "utf-8");
|
|
43787
43809
|
const pattern = buildPromisePattern(promise2);
|
|
@@ -43998,6 +44020,56 @@ async function handleFailedVerification(ctx, input) {
|
|
|
43998
44020
|
}
|
|
43999
44021
|
|
|
44000
44022
|
// src/hooks/ralph-loop/pending-verification-handler.ts
|
|
44023
|
+
var ORACLE_AGENT_PATTERN = /Agent:\s*oracle/i;
|
|
44024
|
+
var TASK_METADATA_SESSION_PATTERN = /<task_metadata>[\s\S]*?session_id:\s*([^\s<]+)[\s\S]*?<\/task_metadata>/i;
|
|
44025
|
+
var VERIFIED_PROMISE_PATTERN = new RegExp(`<promise>\\s*${ULTRAWORK_VERIFICATION_PROMISE}\\s*<\\/promise>`, "i");
|
|
44026
|
+
function collectAssistantText(message) {
|
|
44027
|
+
if (!Array.isArray(message.parts)) {
|
|
44028
|
+
return "";
|
|
44029
|
+
}
|
|
44030
|
+
let text = "";
|
|
44031
|
+
for (const part of message.parts) {
|
|
44032
|
+
if (part.type !== "text") {
|
|
44033
|
+
continue;
|
|
44034
|
+
}
|
|
44035
|
+
text += `${text ? `
|
|
44036
|
+
` : ""}${part.text ?? ""}`;
|
|
44037
|
+
}
|
|
44038
|
+
return text;
|
|
44039
|
+
}
|
|
44040
|
+
async function detectOracleVerificationFromParentSession(ctx, parentSessionID, directory, apiTimeoutMs) {
|
|
44041
|
+
try {
|
|
44042
|
+
const response = await withTimeout(ctx.client.session.messages({
|
|
44043
|
+
path: { id: parentSessionID },
|
|
44044
|
+
query: { directory }
|
|
44045
|
+
}), apiTimeoutMs);
|
|
44046
|
+
const messagesResponse = response;
|
|
44047
|
+
const responseData = typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse ? messagesResponse.data : undefined;
|
|
44048
|
+
const messageArray = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
|
|
44049
|
+
for (let index = messageArray.length - 1;index >= 0; index -= 1) {
|
|
44050
|
+
const message = messageArray[index];
|
|
44051
|
+
if (message.info?.role !== "assistant") {
|
|
44052
|
+
continue;
|
|
44053
|
+
}
|
|
44054
|
+
const assistantText = collectAssistantText(message);
|
|
44055
|
+
if (!VERIFIED_PROMISE_PATTERN.test(assistantText) || !ORACLE_AGENT_PATTERN.test(assistantText)) {
|
|
44056
|
+
continue;
|
|
44057
|
+
}
|
|
44058
|
+
const sessionMatch = assistantText.match(TASK_METADATA_SESSION_PATTERN);
|
|
44059
|
+
const detectedOracleSessionID = sessionMatch?.[1]?.trim();
|
|
44060
|
+
if (detectedOracleSessionID) {
|
|
44061
|
+
return detectedOracleSessionID;
|
|
44062
|
+
}
|
|
44063
|
+
}
|
|
44064
|
+
return;
|
|
44065
|
+
} catch (error48) {
|
|
44066
|
+
log(`[${HOOK_NAME3}] Failed to scan parent session for oracle verification evidence`, {
|
|
44067
|
+
parentSessionID,
|
|
44068
|
+
error: String(error48)
|
|
44069
|
+
});
|
|
44070
|
+
return;
|
|
44071
|
+
}
|
|
44072
|
+
}
|
|
44001
44073
|
async function handlePendingVerification(ctx, input) {
|
|
44002
44074
|
const {
|
|
44003
44075
|
sessionID,
|
|
@@ -44010,6 +44082,19 @@ async function handlePendingVerification(ctx, input) {
|
|
|
44010
44082
|
apiTimeoutMs
|
|
44011
44083
|
} = input;
|
|
44012
44084
|
if (matchesParentSession || verificationSessionID && matchesVerificationSession) {
|
|
44085
|
+
if (!verificationSessionID && state3.session_id) {
|
|
44086
|
+
const recoveredVerificationSessionID = await detectOracleVerificationFromParentSession(ctx, state3.session_id, directory, apiTimeoutMs);
|
|
44087
|
+
if (recoveredVerificationSessionID) {
|
|
44088
|
+
const updatedState = loopState.setVerificationSessionID(state3.session_id, recoveredVerificationSessionID);
|
|
44089
|
+
if (updatedState) {
|
|
44090
|
+
log(`[${HOOK_NAME3}] Recovered missing verification session from parent evidence`, {
|
|
44091
|
+
parentSessionID: state3.session_id,
|
|
44092
|
+
recoveredVerificationSessionID
|
|
44093
|
+
});
|
|
44094
|
+
return;
|
|
44095
|
+
}
|
|
44096
|
+
}
|
|
44097
|
+
}
|
|
44013
44098
|
const restarted = await handleFailedVerification(ctx, {
|
|
44014
44099
|
state: state3,
|
|
44015
44100
|
loopState,
|
|
@@ -44141,6 +44226,12 @@ function createRalphLoopEventHandler(ctx, options) {
|
|
|
44141
44226
|
return;
|
|
44142
44227
|
}
|
|
44143
44228
|
if (state3.verification_pending) {
|
|
44229
|
+
if (!verificationSessionID && matchesParentSession) {
|
|
44230
|
+
log(`[${HOOK_NAME3}] Verification pending without tracked oracle session, running recovery check`, {
|
|
44231
|
+
sessionID,
|
|
44232
|
+
iteration: state3.iteration
|
|
44233
|
+
});
|
|
44234
|
+
}
|
|
44144
44235
|
await handlePendingVerification(ctx, {
|
|
44145
44236
|
sessionID,
|
|
44146
44237
|
state: state3,
|
|
@@ -44664,7 +44755,7 @@ function findSlashCommandPartIndex(parts) {
|
|
|
44664
44755
|
// src/hooks/auto-slash-command/executor.ts
|
|
44665
44756
|
import { dirname as dirname14 } from "path";
|
|
44666
44757
|
// src/features/opencode-skill-loader/loader.ts
|
|
44667
|
-
import { join as
|
|
44758
|
+
import { join as join62 } from "path";
|
|
44668
44759
|
import { homedir as homedir11 } from "os";
|
|
44669
44760
|
|
|
44670
44761
|
// src/features/opencode-skill-loader/skill-definition-record.ts
|
|
@@ -44692,7 +44783,7 @@ function deduplicateSkillsByName(skills) {
|
|
|
44692
44783
|
|
|
44693
44784
|
// src/features/opencode-skill-loader/skill-directory-loader.ts
|
|
44694
44785
|
import { promises as fs16 } from "fs";
|
|
44695
|
-
import { join as
|
|
44786
|
+
import { join as join61 } from "path";
|
|
44696
44787
|
|
|
44697
44788
|
// src/features/opencode-skill-loader/loaded-skill-from-path.ts
|
|
44698
44789
|
import { promises as fs15 } from "fs";
|
|
@@ -44710,7 +44801,7 @@ function parseAllowedTools(allowedTools) {
|
|
|
44710
44801
|
|
|
44711
44802
|
// src/features/opencode-skill-loader/skill-mcp-config.ts
|
|
44712
44803
|
import { promises as fs14 } from "fs";
|
|
44713
|
-
import { join as
|
|
44804
|
+
import { join as join60 } from "path";
|
|
44714
44805
|
function parseSkillMcpConfigFromFrontmatter(content) {
|
|
44715
44806
|
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
44716
44807
|
if (!frontmatterMatch)
|
|
@@ -44726,7 +44817,7 @@ function parseSkillMcpConfigFromFrontmatter(content) {
|
|
|
44726
44817
|
return;
|
|
44727
44818
|
}
|
|
44728
44819
|
async function loadMcpJsonFromDir(skillDir) {
|
|
44729
|
-
const mcpJsonPath =
|
|
44820
|
+
const mcpJsonPath = join60(skillDir, "mcp.json");
|
|
44730
44821
|
try {
|
|
44731
44822
|
const content = await fs14.readFile(mcpJsonPath, "utf-8");
|
|
44732
44823
|
const parsed = JSON.parse(content);
|
|
@@ -44815,10 +44906,10 @@ async function loadSkillsFromDir(options) {
|
|
|
44815
44906
|
const directories = entries.filter((entry) => !entry.name.startsWith(".") && (entry.isDirectory() || entry.isSymbolicLink()));
|
|
44816
44907
|
const files = entries.filter((entry) => !entry.name.startsWith(".") && !entry.isDirectory() && !entry.isSymbolicLink() && isMarkdownFile(entry));
|
|
44817
44908
|
for (const entry of directories) {
|
|
44818
|
-
const entryPath =
|
|
44909
|
+
const entryPath = join61(options.skillsDir, entry.name);
|
|
44819
44910
|
const resolvedPath = await resolveSymlinkAsync(entryPath);
|
|
44820
44911
|
const dirName = entry.name;
|
|
44821
|
-
const skillMdPath =
|
|
44912
|
+
const skillMdPath = join61(resolvedPath, "SKILL.md");
|
|
44822
44913
|
try {
|
|
44823
44914
|
await fs16.access(skillMdPath);
|
|
44824
44915
|
const skill = await loadSkillFromPath({
|
|
@@ -44833,7 +44924,7 @@ async function loadSkillsFromDir(options) {
|
|
|
44833
44924
|
}
|
|
44834
44925
|
continue;
|
|
44835
44926
|
} catch {}
|
|
44836
|
-
const namedSkillMdPath =
|
|
44927
|
+
const namedSkillMdPath = join61(resolvedPath, `${dirName}.md`);
|
|
44837
44928
|
try {
|
|
44838
44929
|
await fs16.access(namedSkillMdPath);
|
|
44839
44930
|
const skill = await loadSkillFromPath({
|
|
@@ -44865,7 +44956,7 @@ async function loadSkillsFromDir(options) {
|
|
|
44865
44956
|
}
|
|
44866
44957
|
}
|
|
44867
44958
|
for (const entry of files) {
|
|
44868
|
-
const entryPath =
|
|
44959
|
+
const entryPath = join61(options.skillsDir, entry.name);
|
|
44869
44960
|
const baseName = inferSkillNameFromFileName(entryPath);
|
|
44870
44961
|
const skill = await loadSkillFromPath({
|
|
44871
44962
|
skillPath: entryPath,
|
|
@@ -44883,12 +44974,12 @@ async function loadSkillsFromDir(options) {
|
|
|
44883
44974
|
|
|
44884
44975
|
// src/features/opencode-skill-loader/loader.ts
|
|
44885
44976
|
async function loadUserSkills() {
|
|
44886
|
-
const userSkillsDir =
|
|
44977
|
+
const userSkillsDir = join62(getClaudeConfigDir(), "skills");
|
|
44887
44978
|
const skills = await loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
|
|
44888
44979
|
return skillsToCommandDefinitionRecord(skills);
|
|
44889
44980
|
}
|
|
44890
44981
|
async function loadProjectSkills(directory) {
|
|
44891
|
-
const projectSkillsDir =
|
|
44982
|
+
const projectSkillsDir = join62(directory ?? process.cwd(), ".claude", "skills");
|
|
44892
44983
|
const skills = await loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" });
|
|
44893
44984
|
return skillsToCommandDefinitionRecord(skills);
|
|
44894
44985
|
}
|
|
@@ -44898,7 +44989,7 @@ async function loadOpencodeGlobalSkills() {
|
|
|
44898
44989
|
return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat()));
|
|
44899
44990
|
}
|
|
44900
44991
|
async function loadOpencodeProjectSkills(directory) {
|
|
44901
|
-
const opencodeProjectDir =
|
|
44992
|
+
const opencodeProjectDir = join62(directory ?? process.cwd(), ".opencode", "skills");
|
|
44902
44993
|
const skills = await loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" });
|
|
44903
44994
|
return skillsToCommandDefinitionRecord(skills);
|
|
44904
44995
|
}
|
|
@@ -44945,11 +45036,11 @@ async function discoverSkills(options = {}) {
|
|
|
44945
45036
|
]);
|
|
44946
45037
|
}
|
|
44947
45038
|
async function discoverUserClaudeSkills() {
|
|
44948
|
-
const userSkillsDir =
|
|
45039
|
+
const userSkillsDir = join62(getClaudeConfigDir(), "skills");
|
|
44949
45040
|
return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
|
|
44950
45041
|
}
|
|
44951
45042
|
async function discoverProjectClaudeSkills(directory) {
|
|
44952
|
-
const projectSkillsDir =
|
|
45043
|
+
const projectSkillsDir = join62(directory ?? process.cwd(), ".claude", "skills");
|
|
44953
45044
|
return loadSkillsFromDir({ skillsDir: projectSkillsDir, scope: "project" });
|
|
44954
45045
|
}
|
|
44955
45046
|
async function discoverOpencodeGlobalSkills() {
|
|
@@ -44958,15 +45049,15 @@ async function discoverOpencodeGlobalSkills() {
|
|
|
44958
45049
|
return deduplicateSkillsByName(allSkills.flat());
|
|
44959
45050
|
}
|
|
44960
45051
|
async function discoverOpencodeProjectSkills(directory) {
|
|
44961
|
-
const opencodeProjectDir =
|
|
45052
|
+
const opencodeProjectDir = join62(directory ?? process.cwd(), ".opencode", "skills");
|
|
44962
45053
|
return loadSkillsFromDir({ skillsDir: opencodeProjectDir, scope: "opencode-project" });
|
|
44963
45054
|
}
|
|
44964
45055
|
async function discoverProjectAgentsSkills(directory) {
|
|
44965
|
-
const agentsProjectDir =
|
|
45056
|
+
const agentsProjectDir = join62(directory ?? process.cwd(), ".agents", "skills");
|
|
44966
45057
|
return loadSkillsFromDir({ skillsDir: agentsProjectDir, scope: "project" });
|
|
44967
45058
|
}
|
|
44968
45059
|
async function discoverGlobalAgentsSkills() {
|
|
44969
|
-
const agentsGlobalDir =
|
|
45060
|
+
const agentsGlobalDir = join62(homedir11(), ".agents", "skills");
|
|
44970
45061
|
return loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
|
|
44971
45062
|
}
|
|
44972
45063
|
// src/features/opencode-skill-loader/merger/builtin-skill-converter.ts
|
|
@@ -44993,7 +45084,7 @@ function builtinToLoadedSkill(builtin) {
|
|
|
44993
45084
|
}
|
|
44994
45085
|
|
|
44995
45086
|
// src/features/opencode-skill-loader/merger/config-skill-entry-loader.ts
|
|
44996
|
-
import { existsSync as
|
|
45087
|
+
import { existsSync as existsSync53, readFileSync as readFileSync37 } from "fs";
|
|
44997
45088
|
import { dirname as dirname12, isAbsolute as isAbsolute4, resolve as resolve5 } from "path";
|
|
44998
45089
|
import { homedir as homedir12 } from "os";
|
|
44999
45090
|
function resolveFilePath5(from, configDir) {
|
|
@@ -45012,7 +45103,7 @@ function resolveFilePath5(from, configDir) {
|
|
|
45012
45103
|
}
|
|
45013
45104
|
function loadSkillFromFile(filePath) {
|
|
45014
45105
|
try {
|
|
45015
|
-
if (!
|
|
45106
|
+
if (!existsSync53(filePath))
|
|
45016
45107
|
return null;
|
|
45017
45108
|
const content = readFileSync37(filePath, "utf-8");
|
|
45018
45109
|
const { data, body } = parseFrontmatter(content);
|
|
@@ -47508,6 +47599,11 @@ var BabysittingConfigSchema = exports_external.object({
|
|
|
47508
47599
|
timeout_ms: exports_external.number().default(120000)
|
|
47509
47600
|
});
|
|
47510
47601
|
// src/config/schema/background-task.ts
|
|
47602
|
+
var CircuitBreakerConfigSchema = exports_external.object({
|
|
47603
|
+
maxToolCalls: exports_external.number().int().min(10).optional(),
|
|
47604
|
+
windowSize: exports_external.number().int().min(5).optional(),
|
|
47605
|
+
repetitionThresholdPercent: exports_external.number().gt(0).max(100).optional()
|
|
47606
|
+
});
|
|
47511
47607
|
var BackgroundTaskConfigSchema = exports_external.object({
|
|
47512
47608
|
defaultConcurrency: exports_external.number().min(1).optional(),
|
|
47513
47609
|
providerConcurrency: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional(),
|
|
@@ -47516,7 +47612,9 @@ var BackgroundTaskConfigSchema = exports_external.object({
|
|
|
47516
47612
|
maxDescendants: exports_external.number().int().min(1).optional(),
|
|
47517
47613
|
staleTimeoutMs: exports_external.number().min(60000).optional(),
|
|
47518
47614
|
messageStalenessTimeoutMs: exports_external.number().min(60000).optional(),
|
|
47519
|
-
syncPollTimeoutMs: exports_external.number().min(60000).optional()
|
|
47615
|
+
syncPollTimeoutMs: exports_external.number().min(60000).optional(),
|
|
47616
|
+
maxToolCalls: exports_external.number().int().min(10).optional(),
|
|
47617
|
+
circuitBreaker: CircuitBreakerConfigSchema.optional()
|
|
47520
47618
|
});
|
|
47521
47619
|
// src/config/schema/browser-automation.ts
|
|
47522
47620
|
var BrowserAutomationProviderSchema = exports_external.enum([
|
|
@@ -47902,7 +48000,14 @@ function prefixGitCommandsInBashCodeBlocks(template, prefix) {
|
|
|
47902
48000
|
});
|
|
47903
48001
|
}
|
|
47904
48002
|
function prefixGitCommandsInCodeBlock(codeBlock, prefix) {
|
|
47905
|
-
return codeBlock.
|
|
48003
|
+
return codeBlock.split(`
|
|
48004
|
+
`).map((line) => {
|
|
48005
|
+
if (line.includes(prefix)) {
|
|
48006
|
+
return line;
|
|
48007
|
+
}
|
|
48008
|
+
return line.replace(LEADING_GIT_COMMAND_PATTERN, `$1${prefix} git`).replace(INLINE_GIT_COMMAND_PATTERN, `$1${prefix} git`);
|
|
48009
|
+
}).join(`
|
|
48010
|
+
`);
|
|
47906
48011
|
}
|
|
47907
48012
|
function buildCommitFooterInjection(commitFooter, includeCoAuthoredBy, gitEnvPrefix) {
|
|
47908
48013
|
const sections = [];
|
|
@@ -47996,7 +48101,7 @@ async function resolveMultipleSkillsAsync(skillNames, options) {
|
|
|
47996
48101
|
// src/features/opencode-skill-loader/config-source-discovery.ts
|
|
47997
48102
|
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
47998
48103
|
import { promises as fs17 } from "fs";
|
|
47999
|
-
import { dirname as dirname13, extname, isAbsolute as isAbsolute5, join as
|
|
48104
|
+
import { dirname as dirname13, extname, isAbsolute as isAbsolute5, join as join63, relative as relative3 } from "path";
|
|
48000
48105
|
var MAX_RECURSIVE_DEPTH = 10;
|
|
48001
48106
|
function isHttpUrl(path11) {
|
|
48002
48107
|
return path11.startsWith("http://") || path11.startsWith("https://");
|
|
@@ -48005,7 +48110,7 @@ function toAbsolutePath(path11, configDir) {
|
|
|
48005
48110
|
if (isAbsolute5(path11)) {
|
|
48006
48111
|
return path11;
|
|
48007
48112
|
}
|
|
48008
|
-
return
|
|
48113
|
+
return join63(configDir, path11);
|
|
48009
48114
|
}
|
|
48010
48115
|
function isMarkdownPath(path11) {
|
|
48011
48116
|
return extname(path11).toLowerCase() === ".md";
|
|
@@ -48075,8 +48180,8 @@ async function discoverConfigSourceSkills(options) {
|
|
|
48075
48180
|
return deduplicateSkillsByName(loadedBySource.flat());
|
|
48076
48181
|
}
|
|
48077
48182
|
// src/tools/slashcommand/command-discovery.ts
|
|
48078
|
-
import { existsSync as
|
|
48079
|
-
import { basename as basename5, join as
|
|
48183
|
+
import { existsSync as existsSync54, readdirSync as readdirSync15, readFileSync as readFileSync39 } from "fs";
|
|
48184
|
+
import { basename as basename5, join as join64 } from "path";
|
|
48080
48185
|
// src/features/builtin-commands/templates/init-deep.ts
|
|
48081
48186
|
var INIT_DEEP_TEMPLATE = `# /init-deep
|
|
48082
48187
|
|
|
@@ -49459,14 +49564,14 @@ function loadBuiltinCommands(disabledCommands) {
|
|
|
49459
49564
|
}
|
|
49460
49565
|
// src/tools/slashcommand/command-discovery.ts
|
|
49461
49566
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
49462
|
-
if (!
|
|
49567
|
+
if (!existsSync54(commandsDir))
|
|
49463
49568
|
return [];
|
|
49464
49569
|
const entries = readdirSync15(commandsDir, { withFileTypes: true });
|
|
49465
49570
|
const commands3 = [];
|
|
49466
49571
|
for (const entry of entries) {
|
|
49467
49572
|
if (!isMarkdownFile(entry))
|
|
49468
49573
|
continue;
|
|
49469
|
-
const commandPath =
|
|
49574
|
+
const commandPath = join64(commandsDir, entry.name);
|
|
49470
49575
|
const commandName = basename5(entry.name, ".md");
|
|
49471
49576
|
try {
|
|
49472
49577
|
const content = readFileSync39(commandPath, "utf-8");
|
|
@@ -49509,10 +49614,10 @@ function discoverPluginCommands(options) {
|
|
|
49509
49614
|
}));
|
|
49510
49615
|
}
|
|
49511
49616
|
function discoverCommandsSync(directory, options) {
|
|
49512
|
-
const userCommandsDir =
|
|
49513
|
-
const projectCommandsDir =
|
|
49617
|
+
const userCommandsDir = join64(getClaudeConfigDir(), "commands");
|
|
49618
|
+
const projectCommandsDir = join64(directory ?? process.cwd(), ".claude", "commands");
|
|
49514
49619
|
const opencodeGlobalDirs = getOpenCodeCommandDirs({ binary: "opencode" });
|
|
49515
|
-
const opencodeProjectDir =
|
|
49620
|
+
const opencodeProjectDir = join64(directory ?? process.cwd(), ".opencode", "command");
|
|
49516
49621
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
49517
49622
|
const opencodeGlobalCommands = opencodeGlobalDirs.flatMap((commandsDir) => discoverCommandsFromDir(commandsDir, "opencode"));
|
|
49518
49623
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -49974,14 +50079,14 @@ var NOTEPAD_DIR = "notepads";
|
|
|
49974
50079
|
var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
|
|
49975
50080
|
var PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
|
|
49976
50081
|
// src/features/boulder-state/storage.ts
|
|
49977
|
-
import { existsSync as
|
|
49978
|
-
import { dirname as dirname15, join as
|
|
50082
|
+
import { existsSync as existsSync55, readFileSync as readFileSync40, writeFileSync as writeFileSync17, mkdirSync as mkdirSync13, readdirSync as readdirSync16 } from "fs";
|
|
50083
|
+
import { dirname as dirname15, join as join65, basename as basename6 } from "path";
|
|
49979
50084
|
function getBoulderFilePath(directory) {
|
|
49980
|
-
return
|
|
50085
|
+
return join65(directory, BOULDER_DIR, BOULDER_FILE);
|
|
49981
50086
|
}
|
|
49982
50087
|
function readBoulderState(directory) {
|
|
49983
50088
|
const filePath = getBoulderFilePath(directory);
|
|
49984
|
-
if (!
|
|
50089
|
+
if (!existsSync55(filePath)) {
|
|
49985
50090
|
return null;
|
|
49986
50091
|
}
|
|
49987
50092
|
try {
|
|
@@ -50002,7 +50107,7 @@ function writeBoulderState(directory, state3) {
|
|
|
50002
50107
|
const filePath = getBoulderFilePath(directory);
|
|
50003
50108
|
try {
|
|
50004
50109
|
const dir = dirname15(filePath);
|
|
50005
|
-
if (!
|
|
50110
|
+
if (!existsSync55(dir)) {
|
|
50006
50111
|
mkdirSync13(dir, { recursive: true });
|
|
50007
50112
|
}
|
|
50008
50113
|
writeFileSync17(filePath, JSON.stringify(state3, null, 2), "utf-8");
|
|
@@ -50019,17 +50124,20 @@ function appendSessionId(directory, sessionId) {
|
|
|
50019
50124
|
if (!Array.isArray(state3.session_ids)) {
|
|
50020
50125
|
state3.session_ids = [];
|
|
50021
50126
|
}
|
|
50127
|
+
const originalSessionIds = [...state3.session_ids];
|
|
50022
50128
|
state3.session_ids.push(sessionId);
|
|
50023
50129
|
if (writeBoulderState(directory, state3)) {
|
|
50024
50130
|
return state3;
|
|
50025
50131
|
}
|
|
50132
|
+
state3.session_ids = originalSessionIds;
|
|
50133
|
+
return null;
|
|
50026
50134
|
}
|
|
50027
50135
|
return state3;
|
|
50028
50136
|
}
|
|
50029
50137
|
function clearBoulderState(directory) {
|
|
50030
50138
|
const filePath = getBoulderFilePath(directory);
|
|
50031
50139
|
try {
|
|
50032
|
-
if (
|
|
50140
|
+
if (existsSync55(filePath)) {
|
|
50033
50141
|
const { unlinkSync: unlinkSync11 } = __require("fs");
|
|
50034
50142
|
unlinkSync11(filePath);
|
|
50035
50143
|
}
|
|
@@ -50039,13 +50147,13 @@ function clearBoulderState(directory) {
|
|
|
50039
50147
|
}
|
|
50040
50148
|
}
|
|
50041
50149
|
function findPrometheusPlans(directory) {
|
|
50042
|
-
const plansDir =
|
|
50043
|
-
if (!
|
|
50150
|
+
const plansDir = join65(directory, PROMETHEUS_PLANS_DIR);
|
|
50151
|
+
if (!existsSync55(plansDir)) {
|
|
50044
50152
|
return [];
|
|
50045
50153
|
}
|
|
50046
50154
|
try {
|
|
50047
50155
|
const files = readdirSync16(plansDir);
|
|
50048
|
-
return files.filter((f) => f.endsWith(".md")).map((f) =>
|
|
50156
|
+
return files.filter((f) => f.endsWith(".md")).map((f) => join65(plansDir, f)).sort((a, b) => {
|
|
50049
50157
|
const aStat = __require("fs").statSync(a);
|
|
50050
50158
|
const bStat = __require("fs").statSync(b);
|
|
50051
50159
|
return bStat.mtimeMs - aStat.mtimeMs;
|
|
@@ -50055,7 +50163,7 @@ function findPrometheusPlans(directory) {
|
|
|
50055
50163
|
}
|
|
50056
50164
|
}
|
|
50057
50165
|
function getPlanProgress(planPath) {
|
|
50058
|
-
if (!
|
|
50166
|
+
if (!existsSync55(planPath)) {
|
|
50059
50167
|
return { total: 0, completed: 0, isComplete: true };
|
|
50060
50168
|
}
|
|
50061
50169
|
try {
|
|
@@ -51135,7 +51243,7 @@ function createAtlasEventHandler(input) {
|
|
|
51135
51243
|
init_logger();
|
|
51136
51244
|
|
|
51137
51245
|
// src/hooks/atlas/final-wave-plan-state.ts
|
|
51138
|
-
import { existsSync as
|
|
51246
|
+
import { existsSync as existsSync56, readFileSync as readFileSync41 } from "fs";
|
|
51139
51247
|
var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
|
|
51140
51248
|
var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
|
|
51141
51249
|
var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
|
|
@@ -51143,7 +51251,7 @@ var UNCHECKED_CHECKBOX_PATTERN = /^\s*[-*]\s*\[\s*\]\s*(.+)$/;
|
|
|
51143
51251
|
var TODO_TASK_PATTERN = /^\d+\./;
|
|
51144
51252
|
var FINAL_WAVE_TASK_PATTERN = /^F\d+\./i;
|
|
51145
51253
|
function readFinalWavePlanState(planPath) {
|
|
51146
|
-
if (!
|
|
51254
|
+
if (!existsSync56(planPath)) {
|
|
51147
51255
|
return null;
|
|
51148
51256
|
}
|
|
51149
51257
|
try {
|
|
@@ -53336,8 +53444,14 @@ function extractStatusCode(error48, retryOnErrors) {
|
|
|
53336
53444
|
if (!error48)
|
|
53337
53445
|
return;
|
|
53338
53446
|
const errorObj = error48;
|
|
53339
|
-
const statusCode =
|
|
53340
|
-
|
|
53447
|
+
const statusCode = [
|
|
53448
|
+
errorObj.statusCode,
|
|
53449
|
+
errorObj.status,
|
|
53450
|
+
errorObj.data?.statusCode,
|
|
53451
|
+
errorObj.error?.statusCode,
|
|
53452
|
+
errorObj.cause?.statusCode
|
|
53453
|
+
].find((code) => typeof code === "number");
|
|
53454
|
+
if (statusCode !== undefined) {
|
|
53341
53455
|
return statusCode;
|
|
53342
53456
|
}
|
|
53343
53457
|
const codes = retryOnErrors ?? DEFAULT_CONFIG2.retry_on_errors;
|
|
@@ -54097,8 +54211,8 @@ function createRuntimeFallbackHook(ctx, options) {
|
|
|
54097
54211
|
};
|
|
54098
54212
|
}
|
|
54099
54213
|
// src/hooks/write-existing-file-guard/hook.ts
|
|
54100
|
-
import { existsSync as
|
|
54101
|
-
import { basename as basename7, dirname as dirname16, isAbsolute as isAbsolute7, join as
|
|
54214
|
+
import { existsSync as existsSync58, realpathSync as realpathSync4 } from "fs";
|
|
54215
|
+
import { basename as basename7, dirname as dirname16, isAbsolute as isAbsolute7, join as join67, normalize, relative as relative5, resolve as resolve7 } from "path";
|
|
54102
54216
|
var MAX_TRACKED_SESSIONS = 256;
|
|
54103
54217
|
var MAX_TRACKED_PATHS_PER_SESSION = 1024;
|
|
54104
54218
|
function asRecord(value) {
|
|
@@ -54119,7 +54233,7 @@ function isPathInsideDirectory(pathToCheck, directory) {
|
|
|
54119
54233
|
}
|
|
54120
54234
|
function toCanonicalPath(absolutePath) {
|
|
54121
54235
|
let canonicalPath = absolutePath;
|
|
54122
|
-
if (
|
|
54236
|
+
if (existsSync58(absolutePath)) {
|
|
54123
54237
|
try {
|
|
54124
54238
|
canonicalPath = realpathSync4.native(absolutePath);
|
|
54125
54239
|
} catch {
|
|
@@ -54127,8 +54241,8 @@ function toCanonicalPath(absolutePath) {
|
|
|
54127
54241
|
}
|
|
54128
54242
|
} else {
|
|
54129
54243
|
const absoluteDir = dirname16(absolutePath);
|
|
54130
|
-
const resolvedDir =
|
|
54131
|
-
canonicalPath =
|
|
54244
|
+
const resolvedDir = existsSync58(absoluteDir) ? realpathSync4.native(absoluteDir) : absoluteDir;
|
|
54245
|
+
canonicalPath = join67(resolvedDir, basename7(absolutePath));
|
|
54132
54246
|
}
|
|
54133
54247
|
return normalize(canonicalPath);
|
|
54134
54248
|
}
|
|
@@ -54228,7 +54342,7 @@ function createWriteExistingFileGuardHook(ctx) {
|
|
|
54228
54342
|
return;
|
|
54229
54343
|
}
|
|
54230
54344
|
if (toolName === "read") {
|
|
54231
|
-
if (!
|
|
54345
|
+
if (!existsSync58(resolvedPath) || !input.sessionID) {
|
|
54232
54346
|
return;
|
|
54233
54347
|
}
|
|
54234
54348
|
registerReadPermission(input.sessionID, canonicalPath);
|
|
@@ -54238,7 +54352,7 @@ function createWriteExistingFileGuardHook(ctx) {
|
|
|
54238
54352
|
if (argsRecord && "overwrite" in argsRecord) {
|
|
54239
54353
|
delete argsRecord.overwrite;
|
|
54240
54354
|
}
|
|
54241
|
-
if (!
|
|
54355
|
+
if (!existsSync58(resolvedPath)) {
|
|
54242
54356
|
return;
|
|
54243
54357
|
}
|
|
54244
54358
|
const isSisyphusPath2 = canonicalPath.includes("/.sisyphus/");
|
|
@@ -55327,12 +55441,12 @@ var DEFAULT_MAX_SYMBOLS = 200;
|
|
|
55327
55441
|
var DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
55328
55442
|
var DEFAULT_MAX_DIRECTORY_FILES = 50;
|
|
55329
55443
|
// src/tools/lsp/server-config-loader.ts
|
|
55330
|
-
import { existsSync as
|
|
55331
|
-
import { join as
|
|
55444
|
+
import { existsSync as existsSync59, readFileSync as readFileSync43 } from "fs";
|
|
55445
|
+
import { join as join68 } from "path";
|
|
55332
55446
|
init_jsonc_parser();
|
|
55333
55447
|
init_plugin_identity();
|
|
55334
55448
|
function loadJsonFile(path12) {
|
|
55335
|
-
if (!
|
|
55449
|
+
if (!existsSync59(path12))
|
|
55336
55450
|
return null;
|
|
55337
55451
|
try {
|
|
55338
55452
|
return parseJsonc(readFileSync43(path12, "utf-8"));
|
|
@@ -55344,9 +55458,9 @@ function getConfigPaths3() {
|
|
|
55344
55458
|
const cwd = process.cwd();
|
|
55345
55459
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
55346
55460
|
return {
|
|
55347
|
-
project: detectManagedConfigFile(
|
|
55461
|
+
project: detectManagedConfigFile(join68(cwd, ".opencode")).path,
|
|
55348
55462
|
user: detectManagedConfigFile(configDir).path,
|
|
55349
|
-
opencode: detectConfigFile(
|
|
55463
|
+
opencode: detectConfigFile(join68(configDir, "opencode")).path
|
|
55350
55464
|
};
|
|
55351
55465
|
}
|
|
55352
55466
|
function loadAllConfigs() {
|
|
@@ -55415,20 +55529,20 @@ function getMergedServers() {
|
|
|
55415
55529
|
}
|
|
55416
55530
|
|
|
55417
55531
|
// src/tools/lsp/server-installation.ts
|
|
55418
|
-
import { existsSync as
|
|
55419
|
-
import { delimiter, join as
|
|
55532
|
+
import { existsSync as existsSync60 } from "fs";
|
|
55533
|
+
import { delimiter, join as join70 } from "path";
|
|
55420
55534
|
|
|
55421
55535
|
// src/tools/lsp/server-path-bases.ts
|
|
55422
|
-
import { join as
|
|
55536
|
+
import { join as join69 } from "path";
|
|
55423
55537
|
function getLspServerAdditionalPathBases(workingDirectory) {
|
|
55424
55538
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
55425
|
-
const dataDir =
|
|
55539
|
+
const dataDir = join69(getDataDir(), "opencode");
|
|
55426
55540
|
return [
|
|
55427
|
-
|
|
55428
|
-
|
|
55429
|
-
|
|
55430
|
-
|
|
55431
|
-
|
|
55541
|
+
join69(workingDirectory, "node_modules", ".bin"),
|
|
55542
|
+
join69(configDir, "bin"),
|
|
55543
|
+
join69(configDir, "node_modules", ".bin"),
|
|
55544
|
+
join69(dataDir, "bin"),
|
|
55545
|
+
join69(dataDir, "bin", "node_modules", ".bin")
|
|
55432
55546
|
];
|
|
55433
55547
|
}
|
|
55434
55548
|
|
|
@@ -55438,7 +55552,7 @@ function isServerInstalled(command) {
|
|
|
55438
55552
|
return false;
|
|
55439
55553
|
const cmd = command[0];
|
|
55440
55554
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
55441
|
-
if (
|
|
55555
|
+
if (existsSync60(cmd))
|
|
55442
55556
|
return true;
|
|
55443
55557
|
}
|
|
55444
55558
|
const isWindows2 = process.platform === "win32";
|
|
@@ -55459,14 +55573,14 @@ function isServerInstalled(command) {
|
|
|
55459
55573
|
const paths = pathEnv.split(delimiter);
|
|
55460
55574
|
for (const p of paths) {
|
|
55461
55575
|
for (const suffix of exts) {
|
|
55462
|
-
if (
|
|
55576
|
+
if (existsSync60(join70(p, cmd + suffix))) {
|
|
55463
55577
|
return true;
|
|
55464
55578
|
}
|
|
55465
55579
|
}
|
|
55466
55580
|
}
|
|
55467
55581
|
for (const base of getLspServerAdditionalPathBases(process.cwd())) {
|
|
55468
55582
|
for (const suffix of exts) {
|
|
55469
|
-
if (
|
|
55583
|
+
if (existsSync60(join70(base, cmd + suffix))) {
|
|
55470
55584
|
return true;
|
|
55471
55585
|
}
|
|
55472
55586
|
}
|
|
@@ -55524,13 +55638,13 @@ function getLanguageId(ext) {
|
|
|
55524
55638
|
init_logger();
|
|
55525
55639
|
var {spawn: bunSpawn2 } = globalThis.Bun;
|
|
55526
55640
|
import { spawn as nodeSpawn2 } from "child_process";
|
|
55527
|
-
import { existsSync as
|
|
55641
|
+
import { existsSync as existsSync61, statSync as statSync7 } from "fs";
|
|
55528
55642
|
function shouldUseNodeSpawn() {
|
|
55529
55643
|
return process.platform === "win32";
|
|
55530
55644
|
}
|
|
55531
55645
|
function validateCwd(cwd) {
|
|
55532
55646
|
try {
|
|
55533
|
-
if (!
|
|
55647
|
+
if (!existsSync61(cwd)) {
|
|
55534
55648
|
return { valid: false, error: `Working directory does not exist: ${cwd}` };
|
|
55535
55649
|
}
|
|
55536
55650
|
const stats = statSync7(cwd);
|
|
@@ -56275,9 +56389,9 @@ var lspManager = LSPServerManager.getInstance();
|
|
|
56275
56389
|
// src/tools/lsp/lsp-client-wrapper.ts
|
|
56276
56390
|
import { extname as extname4, resolve as resolve9 } from "path";
|
|
56277
56391
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
56278
|
-
import { existsSync as
|
|
56392
|
+
import { existsSync as existsSync62, statSync as statSync8 } from "fs";
|
|
56279
56393
|
function isDirectoryPath(filePath) {
|
|
56280
|
-
if (!
|
|
56394
|
+
if (!existsSync62(filePath)) {
|
|
56281
56395
|
return false;
|
|
56282
56396
|
}
|
|
56283
56397
|
return statSync8(filePath).isDirectory();
|
|
@@ -56287,14 +56401,14 @@ function uriToPath(uri) {
|
|
|
56287
56401
|
}
|
|
56288
56402
|
function findWorkspaceRoot(filePath) {
|
|
56289
56403
|
let dir = resolve9(filePath);
|
|
56290
|
-
if (!
|
|
56404
|
+
if (!existsSync62(dir) || !isDirectoryPath(dir)) {
|
|
56291
56405
|
dir = __require("path").dirname(dir);
|
|
56292
56406
|
}
|
|
56293
56407
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
56294
56408
|
let prevDir = "";
|
|
56295
56409
|
while (dir !== prevDir) {
|
|
56296
56410
|
for (const marker of markers) {
|
|
56297
|
-
if (
|
|
56411
|
+
if (existsSync62(__require("path").join(dir, marker))) {
|
|
56298
56412
|
return dir;
|
|
56299
56413
|
}
|
|
56300
56414
|
}
|
|
@@ -69021,8 +69135,8 @@ var lsp_symbols = tool({
|
|
|
69021
69135
|
import { resolve as resolve11 } from "path";
|
|
69022
69136
|
|
|
69023
69137
|
// src/tools/lsp/directory-diagnostics.ts
|
|
69024
|
-
import { existsSync as
|
|
69025
|
-
import { extname as extname5, join as
|
|
69138
|
+
import { existsSync as existsSync63, lstatSync as lstatSync2, readdirSync as readdirSync17 } from "fs";
|
|
69139
|
+
import { extname as extname5, join as join71, resolve as resolve10 } from "path";
|
|
69026
69140
|
var SKIP_DIRECTORIES = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
|
|
69027
69141
|
function collectFilesWithExtension(dir, extension, maxFiles) {
|
|
69028
69142
|
const files = [];
|
|
@@ -69038,7 +69152,7 @@ function collectFilesWithExtension(dir, extension, maxFiles) {
|
|
|
69038
69152
|
for (const entry of entries) {
|
|
69039
69153
|
if (files.length >= maxFiles)
|
|
69040
69154
|
return;
|
|
69041
|
-
const fullPath =
|
|
69155
|
+
const fullPath = join71(currentDir, entry);
|
|
69042
69156
|
let stat;
|
|
69043
69157
|
try {
|
|
69044
69158
|
stat = lstatSync2(fullPath);
|
|
@@ -69067,7 +69181,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
69067
69181
|
throw new Error(`Extension must start with a dot (e.g., ".ts", not "${extension}"). ` + `Use ".${extension}" instead.`);
|
|
69068
69182
|
}
|
|
69069
69183
|
const absDir = resolve10(directory);
|
|
69070
|
-
if (!
|
|
69184
|
+
if (!existsSync63(absDir)) {
|
|
69071
69185
|
throw new Error(`Directory does not exist: ${absDir}`);
|
|
69072
69186
|
}
|
|
69073
69187
|
const serverResult = findServerForExtension(extension);
|
|
@@ -69269,12 +69383,12 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
69269
69383
|
|
|
69270
69384
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
69271
69385
|
import { createRequire as createRequire4 } from "module";
|
|
69272
|
-
import { dirname as dirname17, join as
|
|
69273
|
-
import { existsSync as
|
|
69386
|
+
import { dirname as dirname17, join as join73 } from "path";
|
|
69387
|
+
import { existsSync as existsSync65, statSync as statSync9 } from "fs";
|
|
69274
69388
|
|
|
69275
69389
|
// src/tools/ast-grep/downloader.ts
|
|
69276
|
-
import { existsSync as
|
|
69277
|
-
import { join as
|
|
69390
|
+
import { existsSync as existsSync64 } from "fs";
|
|
69391
|
+
import { join as join72 } from "path";
|
|
69278
69392
|
import { homedir as homedir13 } from "os";
|
|
69279
69393
|
import { createRequire as createRequire3 } from "module";
|
|
69280
69394
|
init_logger();
|
|
@@ -69302,12 +69416,12 @@ var PLATFORM_MAP2 = {
|
|
|
69302
69416
|
function getCacheDir3() {
|
|
69303
69417
|
if (process.platform === "win32") {
|
|
69304
69418
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
69305
|
-
const base2 = localAppData ||
|
|
69306
|
-
return
|
|
69419
|
+
const base2 = localAppData || join72(homedir13(), "AppData", "Local");
|
|
69420
|
+
return join72(base2, CACHE_DIR_NAME, "bin");
|
|
69307
69421
|
}
|
|
69308
69422
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
69309
|
-
const base = xdgCache ||
|
|
69310
|
-
return
|
|
69423
|
+
const base = xdgCache || join72(homedir13(), ".cache");
|
|
69424
|
+
return join72(base, CACHE_DIR_NAME, "bin");
|
|
69311
69425
|
}
|
|
69312
69426
|
function getBinaryName3() {
|
|
69313
69427
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
@@ -69324,8 +69438,8 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
69324
69438
|
}
|
|
69325
69439
|
const cacheDir = getCacheDir3();
|
|
69326
69440
|
const binaryName = getBinaryName3();
|
|
69327
|
-
const binaryPath =
|
|
69328
|
-
if (
|
|
69441
|
+
const binaryPath = join72(cacheDir, binaryName);
|
|
69442
|
+
if (existsSync64(binaryPath)) {
|
|
69329
69443
|
return binaryPath;
|
|
69330
69444
|
}
|
|
69331
69445
|
const { arch, os: os6 } = platformInfo;
|
|
@@ -69333,7 +69447,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
69333
69447
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
|
|
69334
69448
|
log(`[${PLUGIN_NAME}] Downloading ast-grep binary...`);
|
|
69335
69449
|
try {
|
|
69336
|
-
const archivePath =
|
|
69450
|
+
const archivePath = join72(cacheDir, assetName);
|
|
69337
69451
|
ensureCacheDir(cacheDir);
|
|
69338
69452
|
await downloadArchive(downloadUrl, archivePath);
|
|
69339
69453
|
await extractZipArchive(archivePath, cacheDir);
|
|
@@ -69387,8 +69501,8 @@ function findSgCliPathSync() {
|
|
|
69387
69501
|
const require2 = createRequire4(import.meta.url);
|
|
69388
69502
|
const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
|
|
69389
69503
|
const cliDirectory = dirname17(cliPackageJsonPath);
|
|
69390
|
-
const sgPath =
|
|
69391
|
-
if (
|
|
69504
|
+
const sgPath = join73(cliDirectory, binaryName);
|
|
69505
|
+
if (existsSync65(sgPath) && isValidBinary(sgPath)) {
|
|
69392
69506
|
return sgPath;
|
|
69393
69507
|
}
|
|
69394
69508
|
} catch {}
|
|
@@ -69399,8 +69513,8 @@ function findSgCliPathSync() {
|
|
|
69399
69513
|
const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
|
|
69400
69514
|
const packageDirectory = dirname17(packageJsonPath);
|
|
69401
69515
|
const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
69402
|
-
const binaryPath =
|
|
69403
|
-
if (
|
|
69516
|
+
const binaryPath = join73(packageDirectory, astGrepBinaryName);
|
|
69517
|
+
if (existsSync65(binaryPath) && isValidBinary(binaryPath)) {
|
|
69404
69518
|
return binaryPath;
|
|
69405
69519
|
}
|
|
69406
69520
|
} catch {}
|
|
@@ -69408,7 +69522,7 @@ function findSgCliPathSync() {
|
|
|
69408
69522
|
if (process.platform === "darwin") {
|
|
69409
69523
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
69410
69524
|
for (const path12 of homebrewPaths) {
|
|
69411
|
-
if (
|
|
69525
|
+
if (existsSync65(path12) && isValidBinary(path12)) {
|
|
69412
69526
|
return path12;
|
|
69413
69527
|
}
|
|
69414
69528
|
}
|
|
@@ -69432,14 +69546,14 @@ function setSgCliPath(path12) {
|
|
|
69432
69546
|
}
|
|
69433
69547
|
// src/tools/ast-grep/cli.ts
|
|
69434
69548
|
var {spawn: spawn10 } = globalThis.Bun;
|
|
69435
|
-
import { existsSync as
|
|
69549
|
+
import { existsSync as existsSync67 } from "fs";
|
|
69436
69550
|
|
|
69437
69551
|
// src/tools/ast-grep/cli-binary-path-resolution.ts
|
|
69438
|
-
import { existsSync as
|
|
69552
|
+
import { existsSync as existsSync66 } from "fs";
|
|
69439
69553
|
var resolvedCliPath3 = null;
|
|
69440
69554
|
var initPromise3 = null;
|
|
69441
69555
|
async function getAstGrepPath() {
|
|
69442
|
-
if (resolvedCliPath3 !== null &&
|
|
69556
|
+
if (resolvedCliPath3 !== null && existsSync66(resolvedCliPath3)) {
|
|
69443
69557
|
return resolvedCliPath3;
|
|
69444
69558
|
}
|
|
69445
69559
|
if (initPromise3) {
|
|
@@ -69447,7 +69561,7 @@ async function getAstGrepPath() {
|
|
|
69447
69561
|
}
|
|
69448
69562
|
initPromise3 = (async () => {
|
|
69449
69563
|
const syncPath = findSgCliPathSync();
|
|
69450
|
-
if (syncPath &&
|
|
69564
|
+
if (syncPath && existsSync66(syncPath)) {
|
|
69451
69565
|
resolvedCliPath3 = syncPath;
|
|
69452
69566
|
setSgCliPath(syncPath);
|
|
69453
69567
|
return syncPath;
|
|
@@ -69546,7 +69660,7 @@ async function runSg(options) {
|
|
|
69546
69660
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
69547
69661
|
args.push(...paths);
|
|
69548
69662
|
let cliPath = getSgCliPath();
|
|
69549
|
-
if (!cliPath || !
|
|
69663
|
+
if (!cliPath || !existsSync67(cliPath)) {
|
|
69550
69664
|
const downloadedPath = await getAstGrepPath();
|
|
69551
69665
|
if (downloadedPath) {
|
|
69552
69666
|
cliPath = downloadedPath;
|
|
@@ -69800,20 +69914,20 @@ import { resolve as resolve12 } from "path";
|
|
|
69800
69914
|
var {spawn: spawn11 } = globalThis.Bun;
|
|
69801
69915
|
|
|
69802
69916
|
// src/tools/grep/constants.ts
|
|
69803
|
-
import { existsSync as
|
|
69804
|
-
import { join as
|
|
69917
|
+
import { existsSync as existsSync69 } from "fs";
|
|
69918
|
+
import { join as join75, dirname as dirname18 } from "path";
|
|
69805
69919
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
69806
69920
|
|
|
69807
69921
|
// src/tools/grep/downloader.ts
|
|
69808
|
-
import { existsSync as
|
|
69809
|
-
import { join as
|
|
69922
|
+
import { existsSync as existsSync68, readdirSync as readdirSync18 } from "fs";
|
|
69923
|
+
import { join as join74 } from "path";
|
|
69810
69924
|
init_plugin_identity();
|
|
69811
69925
|
function findFileRecursive(dir, filename) {
|
|
69812
69926
|
try {
|
|
69813
69927
|
const entries = readdirSync18(dir, { withFileTypes: true, recursive: true });
|
|
69814
69928
|
for (const entry of entries) {
|
|
69815
69929
|
if (entry.isFile() && entry.name === filename) {
|
|
69816
|
-
return
|
|
69930
|
+
return join74(entry.parentPath ?? dir, entry.name);
|
|
69817
69931
|
}
|
|
69818
69932
|
}
|
|
69819
69933
|
} catch {
|
|
@@ -69834,11 +69948,11 @@ function getPlatformKey() {
|
|
|
69834
69948
|
}
|
|
69835
69949
|
function getInstallDir() {
|
|
69836
69950
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
69837
|
-
return
|
|
69951
|
+
return join74(homeDir, ".cache", CACHE_DIR_NAME, "bin");
|
|
69838
69952
|
}
|
|
69839
69953
|
function getRgPath() {
|
|
69840
69954
|
const isWindows2 = process.platform === "win32";
|
|
69841
|
-
return
|
|
69955
|
+
return join74(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
69842
69956
|
}
|
|
69843
69957
|
async function extractTarGz2(archivePath, destDir) {
|
|
69844
69958
|
const platformKey = getPlatformKey();
|
|
@@ -69855,7 +69969,7 @@ async function extractZip2(archivePath, destDir) {
|
|
|
69855
69969
|
const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
69856
69970
|
const foundPath = findFileRecursive(destDir, binaryName);
|
|
69857
69971
|
if (foundPath) {
|
|
69858
|
-
const destPath =
|
|
69972
|
+
const destPath = join74(destDir, binaryName);
|
|
69859
69973
|
if (foundPath !== destPath) {
|
|
69860
69974
|
const { renameSync: renameSync2 } = await import("fs");
|
|
69861
69975
|
renameSync2(foundPath, destPath);
|
|
@@ -69870,13 +69984,13 @@ async function downloadAndInstallRipgrep() {
|
|
|
69870
69984
|
}
|
|
69871
69985
|
const installDir = getInstallDir();
|
|
69872
69986
|
const rgPath = getRgPath();
|
|
69873
|
-
if (
|
|
69987
|
+
if (existsSync68(rgPath)) {
|
|
69874
69988
|
return rgPath;
|
|
69875
69989
|
}
|
|
69876
69990
|
ensureCacheDir(installDir);
|
|
69877
69991
|
const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
|
|
69878
69992
|
const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
|
|
69879
|
-
const archivePath =
|
|
69993
|
+
const archivePath = join74(installDir, filename);
|
|
69880
69994
|
try {
|
|
69881
69995
|
await downloadArchive(url3, archivePath);
|
|
69882
69996
|
if (config4.extension === "tar.gz") {
|
|
@@ -69885,7 +69999,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
69885
69999
|
await extractZip2(archivePath, installDir);
|
|
69886
70000
|
}
|
|
69887
70001
|
ensureExecutable(rgPath);
|
|
69888
|
-
if (!
|
|
70002
|
+
if (!existsSync68(rgPath)) {
|
|
69889
70003
|
throw new Error("ripgrep binary not found after extraction");
|
|
69890
70004
|
}
|
|
69891
70005
|
return rgPath;
|
|
@@ -69897,7 +70011,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
69897
70011
|
}
|
|
69898
70012
|
function getInstalledRipgrepPath() {
|
|
69899
70013
|
const rgPath = getRgPath();
|
|
69900
|
-
return
|
|
70014
|
+
return existsSync68(rgPath) ? rgPath : null;
|
|
69901
70015
|
}
|
|
69902
70016
|
|
|
69903
70017
|
// src/tools/grep/constants.ts
|
|
@@ -69921,14 +70035,14 @@ function getOpenCodeBundledRg() {
|
|
|
69921
70035
|
const isWindows2 = process.platform === "win32";
|
|
69922
70036
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
69923
70037
|
const candidates = [
|
|
69924
|
-
|
|
69925
|
-
|
|
69926
|
-
|
|
69927
|
-
|
|
69928
|
-
|
|
70038
|
+
join75(getDataDir(), "opencode", "bin", rgName),
|
|
70039
|
+
join75(execDir, rgName),
|
|
70040
|
+
join75(execDir, "bin", rgName),
|
|
70041
|
+
join75(execDir, "..", "bin", rgName),
|
|
70042
|
+
join75(execDir, "..", "libexec", rgName)
|
|
69929
70043
|
];
|
|
69930
70044
|
for (const candidate of candidates) {
|
|
69931
|
-
if (
|
|
70045
|
+
if (existsSync69(candidate)) {
|
|
69932
70046
|
return candidate;
|
|
69933
70047
|
}
|
|
69934
70048
|
}
|
|
@@ -70817,6 +70931,7 @@ function createSkillTool(options = {}) {
|
|
|
70817
70931
|
},
|
|
70818
70932
|
async execute(args, ctx) {
|
|
70819
70933
|
const skills2 = await getSkills();
|
|
70934
|
+
cachedDescription = null;
|
|
70820
70935
|
const commands3 = getCommands();
|
|
70821
70936
|
const requestedName = args.name.replace(/^\//, "");
|
|
70822
70937
|
const matchedSkill = skills2.find((s) => s.name.toLowerCase() === requestedName.toLowerCase());
|
|
@@ -70869,9 +70984,9 @@ function createSkillTool(options = {}) {
|
|
|
70869
70984
|
}
|
|
70870
70985
|
var skill = createSkillTool();
|
|
70871
70986
|
// src/tools/session-manager/constants.ts
|
|
70872
|
-
import { join as
|
|
70873
|
-
var TODO_DIR2 =
|
|
70874
|
-
var TRANSCRIPT_DIR2 =
|
|
70987
|
+
import { join as join76 } from "path";
|
|
70988
|
+
var TODO_DIR2 = join76(getClaudeConfigDir(), "todos");
|
|
70989
|
+
var TRANSCRIPT_DIR2 = join76(getClaudeConfigDir(), "transcripts");
|
|
70875
70990
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
70876
70991
|
|
|
70877
70992
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -70944,9 +71059,9 @@ Has Todos: Yes (12 items, 8 completed)
|
|
|
70944
71059
|
Has Transcript: Yes (234 entries)`;
|
|
70945
71060
|
|
|
70946
71061
|
// src/tools/session-manager/storage.ts
|
|
70947
|
-
import { existsSync as
|
|
71062
|
+
import { existsSync as existsSync70 } from "fs";
|
|
70948
71063
|
import { readdir, readFile } from "fs/promises";
|
|
70949
|
-
import { join as
|
|
71064
|
+
import { join as join77 } from "path";
|
|
70950
71065
|
var sdkClient = null;
|
|
70951
71066
|
function setStorageClient(client2) {
|
|
70952
71067
|
sdkClient = client2;
|
|
@@ -70965,7 +71080,7 @@ async function getMainSessions(options) {
|
|
|
70965
71080
|
return [];
|
|
70966
71081
|
}
|
|
70967
71082
|
}
|
|
70968
|
-
if (!
|
|
71083
|
+
if (!existsSync70(SESSION_STORAGE))
|
|
70969
71084
|
return [];
|
|
70970
71085
|
const sessions = [];
|
|
70971
71086
|
try {
|
|
@@ -70973,13 +71088,13 @@ async function getMainSessions(options) {
|
|
|
70973
71088
|
for (const projectDir of projectDirs) {
|
|
70974
71089
|
if (!projectDir.isDirectory())
|
|
70975
71090
|
continue;
|
|
70976
|
-
const projectPath =
|
|
71091
|
+
const projectPath = join77(SESSION_STORAGE, projectDir.name);
|
|
70977
71092
|
const sessionFiles = await readdir(projectPath);
|
|
70978
71093
|
for (const file3 of sessionFiles) {
|
|
70979
71094
|
if (!file3.endsWith(".json"))
|
|
70980
71095
|
continue;
|
|
70981
71096
|
try {
|
|
70982
|
-
const content = await readFile(
|
|
71097
|
+
const content = await readFile(join77(projectPath, file3), "utf-8");
|
|
70983
71098
|
const meta3 = JSON.parse(content);
|
|
70984
71099
|
if (meta3.parentID)
|
|
70985
71100
|
continue;
|
|
@@ -71006,7 +71121,7 @@ async function getAllSessions() {
|
|
|
71006
71121
|
return [];
|
|
71007
71122
|
}
|
|
71008
71123
|
}
|
|
71009
|
-
if (!
|
|
71124
|
+
if (!existsSync70(MESSAGE_STORAGE))
|
|
71010
71125
|
return [];
|
|
71011
71126
|
const sessions = [];
|
|
71012
71127
|
async function scanDirectory(dir) {
|
|
@@ -71014,7 +71129,7 @@ async function getAllSessions() {
|
|
|
71014
71129
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
71015
71130
|
for (const entry of entries) {
|
|
71016
71131
|
if (entry.isDirectory()) {
|
|
71017
|
-
const sessionPath =
|
|
71132
|
+
const sessionPath = join77(dir, entry.name);
|
|
71018
71133
|
const files = await readdir(sessionPath);
|
|
71019
71134
|
if (files.some((f) => f.endsWith(".json"))) {
|
|
71020
71135
|
sessions.push(entry.name);
|
|
@@ -71075,7 +71190,7 @@ async function readSessionMessages2(sessionID) {
|
|
|
71075
71190
|
}
|
|
71076
71191
|
}
|
|
71077
71192
|
const messageDir = getMessageDir(sessionID);
|
|
71078
|
-
if (!messageDir || !
|
|
71193
|
+
if (!messageDir || !existsSync70(messageDir))
|
|
71079
71194
|
return [];
|
|
71080
71195
|
const messages = [];
|
|
71081
71196
|
try {
|
|
@@ -71084,7 +71199,7 @@ async function readSessionMessages2(sessionID) {
|
|
|
71084
71199
|
if (!file3.endsWith(".json"))
|
|
71085
71200
|
continue;
|
|
71086
71201
|
try {
|
|
71087
|
-
const content = await readFile(
|
|
71202
|
+
const content = await readFile(join77(messageDir, file3), "utf-8");
|
|
71088
71203
|
const meta3 = JSON.parse(content);
|
|
71089
71204
|
const parts = await readParts2(meta3.id);
|
|
71090
71205
|
messages.push({
|
|
@@ -71110,8 +71225,8 @@ async function readSessionMessages2(sessionID) {
|
|
|
71110
71225
|
});
|
|
71111
71226
|
}
|
|
71112
71227
|
async function readParts2(messageID) {
|
|
71113
|
-
const partDir =
|
|
71114
|
-
if (!
|
|
71228
|
+
const partDir = join77(PART_STORAGE, messageID);
|
|
71229
|
+
if (!existsSync70(partDir))
|
|
71115
71230
|
return [];
|
|
71116
71231
|
const parts = [];
|
|
71117
71232
|
try {
|
|
@@ -71120,7 +71235,7 @@ async function readParts2(messageID) {
|
|
|
71120
71235
|
if (!file3.endsWith(".json"))
|
|
71121
71236
|
continue;
|
|
71122
71237
|
try {
|
|
71123
|
-
const content = await readFile(
|
|
71238
|
+
const content = await readFile(join77(partDir, file3), "utf-8");
|
|
71124
71239
|
parts.push(JSON.parse(content));
|
|
71125
71240
|
} catch {
|
|
71126
71241
|
continue;
|
|
@@ -71146,14 +71261,14 @@ async function readSessionTodos(sessionID) {
|
|
|
71146
71261
|
return [];
|
|
71147
71262
|
}
|
|
71148
71263
|
}
|
|
71149
|
-
if (!
|
|
71264
|
+
if (!existsSync70(TODO_DIR2))
|
|
71150
71265
|
return [];
|
|
71151
71266
|
try {
|
|
71152
71267
|
const allFiles = await readdir(TODO_DIR2);
|
|
71153
71268
|
const todoFiles = allFiles.filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
71154
71269
|
for (const file3 of todoFiles) {
|
|
71155
71270
|
try {
|
|
71156
|
-
const content = await readFile(
|
|
71271
|
+
const content = await readFile(join77(TODO_DIR2, file3), "utf-8");
|
|
71157
71272
|
const data = JSON.parse(content);
|
|
71158
71273
|
if (Array.isArray(data)) {
|
|
71159
71274
|
return data.map((item) => ({
|
|
@@ -71173,10 +71288,10 @@ async function readSessionTodos(sessionID) {
|
|
|
71173
71288
|
return [];
|
|
71174
71289
|
}
|
|
71175
71290
|
async function readSessionTranscript(sessionID) {
|
|
71176
|
-
if (!
|
|
71291
|
+
if (!existsSync70(TRANSCRIPT_DIR2))
|
|
71177
71292
|
return 0;
|
|
71178
|
-
const transcriptFile =
|
|
71179
|
-
if (!
|
|
71293
|
+
const transcriptFile = join77(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
71294
|
+
if (!existsSync70(transcriptFile))
|
|
71180
71295
|
return 0;
|
|
71181
71296
|
try {
|
|
71182
71297
|
const content = await readFile(transcriptFile, "utf-8");
|
|
@@ -72806,7 +72921,7 @@ function createCallOmoAgent(ctx, backgroundManager, disabledAgents = [], agentOv
|
|
|
72806
72921
|
}
|
|
72807
72922
|
// src/tools/look-at/constants.ts
|
|
72808
72923
|
var MULTIMODAL_LOOKER_AGENT = "multimodal-looker";
|
|
72809
|
-
var LOOK_AT_DESCRIPTION = `
|
|
72924
|
+
var LOOK_AT_DESCRIPTION = `Extract basic information from media files (PDFs, images, diagrams) when a quick summary suffices over precise reading. Good for simple text-based content extraction without using the Read tool. NEVER use for visual precision, aesthetic evaluation, or exact accuracy \u2014 use Read tool instead for those cases.`;
|
|
72810
72925
|
// src/tools/look-at/tools.ts
|
|
72811
72926
|
import { basename as basename8 } from "path";
|
|
72812
72927
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
@@ -73025,9 +73140,9 @@ async function resolveMultimodalLookerAgentMetadata(ctx) {
|
|
|
73025
73140
|
|
|
73026
73141
|
// src/tools/look-at/image-converter.ts
|
|
73027
73142
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
73028
|
-
import { existsSync as
|
|
73143
|
+
import { existsSync as existsSync71, mkdtempSync, readFileSync as readFileSync46, rmSync as rmSync3, unlinkSync as unlinkSync11, writeFileSync as writeFileSync19 } from "fs";
|
|
73029
73144
|
import { tmpdir as tmpdir6 } from "os";
|
|
73030
|
-
import { dirname as dirname21, join as
|
|
73145
|
+
import { dirname as dirname21, join as join78 } from "path";
|
|
73031
73146
|
var SUPPORTED_FORMATS = new Set([
|
|
73032
73147
|
"image/jpeg",
|
|
73033
73148
|
"image/png",
|
|
@@ -73065,11 +73180,11 @@ function needsConversion(mimeType) {
|
|
|
73065
73180
|
return mimeType.startsWith("image/");
|
|
73066
73181
|
}
|
|
73067
73182
|
function convertImageToJpeg(inputPath, mimeType) {
|
|
73068
|
-
if (!
|
|
73183
|
+
if (!existsSync71(inputPath)) {
|
|
73069
73184
|
throw new Error(`File not found: ${inputPath}`);
|
|
73070
73185
|
}
|
|
73071
|
-
const tempDir = mkdtempSync(
|
|
73072
|
-
const outputPath =
|
|
73186
|
+
const tempDir = mkdtempSync(join78(tmpdir6(), "opencode-img-"));
|
|
73187
|
+
const outputPath = join78(tempDir, "converted.jpg");
|
|
73073
73188
|
log(`[image-converter] Converting ${mimeType} to JPEG: ${inputPath}`);
|
|
73074
73189
|
try {
|
|
73075
73190
|
if (process.platform === "darwin") {
|
|
@@ -73079,7 +73194,7 @@ function convertImageToJpeg(inputPath, mimeType) {
|
|
|
73079
73194
|
encoding: "utf-8",
|
|
73080
73195
|
timeout: CONVERSION_TIMEOUT_MS
|
|
73081
73196
|
});
|
|
73082
|
-
if (
|
|
73197
|
+
if (existsSync71(outputPath)) {
|
|
73083
73198
|
log(`[image-converter] Converted using sips: ${outputPath}`);
|
|
73084
73199
|
return outputPath;
|
|
73085
73200
|
}
|
|
@@ -73094,7 +73209,7 @@ function convertImageToJpeg(inputPath, mimeType) {
|
|
|
73094
73209
|
encoding: "utf-8",
|
|
73095
73210
|
timeout: CONVERSION_TIMEOUT_MS
|
|
73096
73211
|
});
|
|
73097
|
-
if (
|
|
73212
|
+
if (existsSync71(outputPath)) {
|
|
73098
73213
|
log(`[image-converter] Converted using ImageMagick: ${outputPath}`);
|
|
73099
73214
|
return outputPath;
|
|
73100
73215
|
}
|
|
@@ -73107,7 +73222,7 @@ function convertImageToJpeg(inputPath, mimeType) {
|
|
|
73107
73222
|
` + ` RHEL/CentOS: sudo yum install ImageMagick`);
|
|
73108
73223
|
} catch (error92) {
|
|
73109
73224
|
try {
|
|
73110
|
-
if (
|
|
73225
|
+
if (existsSync71(outputPath)) {
|
|
73111
73226
|
unlinkSync11(outputPath);
|
|
73112
73227
|
}
|
|
73113
73228
|
} catch {}
|
|
@@ -73121,11 +73236,11 @@ function convertImageToJpeg(inputPath, mimeType) {
|
|
|
73121
73236
|
function cleanupConvertedImage(filePath) {
|
|
73122
73237
|
try {
|
|
73123
73238
|
const tempDirectory = dirname21(filePath);
|
|
73124
|
-
if (
|
|
73239
|
+
if (existsSync71(filePath)) {
|
|
73125
73240
|
unlinkSync11(filePath);
|
|
73126
73241
|
log(`[image-converter] Cleaned up temporary file: ${filePath}`);
|
|
73127
73242
|
}
|
|
73128
|
-
if (
|
|
73243
|
+
if (existsSync71(tempDirectory)) {
|
|
73129
73244
|
rmSync3(tempDirectory, { recursive: true, force: true });
|
|
73130
73245
|
log(`[image-converter] Cleaned up temporary directory: ${tempDirectory}`);
|
|
73131
73246
|
}
|
|
@@ -73134,9 +73249,9 @@ function cleanupConvertedImage(filePath) {
|
|
|
73134
73249
|
}
|
|
73135
73250
|
}
|
|
73136
73251
|
function convertBase64ImageToJpeg(base64Data, mimeType) {
|
|
73137
|
-
const tempDir = mkdtempSync(
|
|
73252
|
+
const tempDir = mkdtempSync(join78(tmpdir6(), "opencode-b64-"));
|
|
73138
73253
|
const inputExt = mimeType.split("/")[1] || "bin";
|
|
73139
|
-
const inputPath =
|
|
73254
|
+
const inputPath = join78(tempDir, `input.${inputExt}`);
|
|
73140
73255
|
const tempFiles = [inputPath];
|
|
73141
73256
|
try {
|
|
73142
73257
|
const cleanBase64 = base64Data.replace(/^data:[^;]+;base64,/, "");
|
|
@@ -73152,7 +73267,7 @@ function convertBase64ImageToJpeg(base64Data, mimeType) {
|
|
|
73152
73267
|
} catch (error92) {
|
|
73153
73268
|
tempFiles.forEach((file3) => {
|
|
73154
73269
|
try {
|
|
73155
|
-
if (
|
|
73270
|
+
if (existsSync71(file3))
|
|
73156
73271
|
unlinkSync11(file3);
|
|
73157
73272
|
} catch {}
|
|
73158
73273
|
});
|
|
@@ -73671,7 +73786,7 @@ var POLL_INTERVAL_MS = 1000;
|
|
|
73671
73786
|
var MIN_STABILITY_TIME_MS = 1e4;
|
|
73672
73787
|
var STABILITY_POLLS_REQUIRED = 3;
|
|
73673
73788
|
var WAIT_FOR_SESSION_INTERVAL_MS = 100;
|
|
73674
|
-
var WAIT_FOR_SESSION_TIMEOUT_MS =
|
|
73789
|
+
var WAIT_FOR_SESSION_TIMEOUT_MS = 60000;
|
|
73675
73790
|
var DEFAULT_POLL_TIMEOUT_MS = 30 * 60 * 1000;
|
|
73676
73791
|
var MAX_POLL_TIME_MS = DEFAULT_POLL_TIMEOUT_MS;
|
|
73677
73792
|
var SESSION_CONTINUATION_STABILITY_MS = 5000;
|
|
@@ -74663,7 +74778,7 @@ function resolveModelForDelegateTask(input) {
|
|
|
74663
74778
|
return { model: userModel };
|
|
74664
74779
|
}
|
|
74665
74780
|
if (input.availableModels.size === 0 && !hasProviderModelsCache() && !hasConnectedProvidersCache()) {
|
|
74666
|
-
return;
|
|
74781
|
+
return { skipped: true };
|
|
74667
74782
|
}
|
|
74668
74783
|
const categoryDefault = normalizeModel(input.categoryDefaultModel);
|
|
74669
74784
|
const explicitHighBaseModel = categoryDefault ? getExplicitHighBaseModel(categoryDefault) : null;
|
|
@@ -74790,6 +74905,7 @@ Available categories: ${allCategoryNames}`
|
|
|
74790
74905
|
let actualModel;
|
|
74791
74906
|
let modelInfo;
|
|
74792
74907
|
let categoryModel;
|
|
74908
|
+
let isModelResolutionSkipped = false;
|
|
74793
74909
|
const overrideModel = sisyphusJuniorModel;
|
|
74794
74910
|
const explicitCategoryModel = userCategories?.[args.category]?.model;
|
|
74795
74911
|
if (!requirement) {
|
|
@@ -74806,7 +74922,9 @@ Available categories: ${allCategoryNames}`
|
|
|
74806
74922
|
availableModels,
|
|
74807
74923
|
systemDefaultModel
|
|
74808
74924
|
});
|
|
74809
|
-
if (resolution) {
|
|
74925
|
+
if (resolution && "skipped" in resolution) {
|
|
74926
|
+
isModelResolutionSkipped = true;
|
|
74927
|
+
} else if (resolution) {
|
|
74810
74928
|
const { model: resolvedModel2, variant: resolvedVariant } = resolution;
|
|
74811
74929
|
actualModel = resolvedModel2;
|
|
74812
74930
|
if (!parseModelString(actualModel)) {
|
|
@@ -74941,7 +75059,7 @@ Create the work plan directly - that's your job as the planning agent.`
|
|
|
74941
75059
|
availableModels,
|
|
74942
75060
|
systemDefaultModel: undefined
|
|
74943
75061
|
});
|
|
74944
|
-
if (resolution) {
|
|
75062
|
+
if (resolution && !("skipped" in resolution)) {
|
|
74945
75063
|
const normalized = normalizeModelFormat(resolution.model);
|
|
74946
75064
|
if (normalized) {
|
|
74947
75065
|
const variantToUse = agentOverride?.variant ?? resolution.variant;
|
|
@@ -75027,7 +75145,7 @@ function createDelegateTask(options) {
|
|
|
75027
75145
|
Available categories:
|
|
75028
75146
|
${categoryList}
|
|
75029
75147
|
- subagent_type: Use specific agent directly (explore, librarian, oracle, metis, momus)
|
|
75030
|
-
- run_in_background: true=async (returns task_id), false=sync (waits).
|
|
75148
|
+
- run_in_background: REQUIRED. true=async (returns task_id), false=sync (waits). Use background=true ONLY for parallel exploration with 5+ independent queries.
|
|
75031
75149
|
- session_id: Existing Task session to continue (from previous task output). Continues agent with FULL CONTEXT PRESERVED - saves tokens, maintains continuity.
|
|
75032
75150
|
- command: The command that triggered this task (optional, for slash command tracking).
|
|
75033
75151
|
|
|
@@ -75043,7 +75161,7 @@ function createDelegateTask(options) {
|
|
|
75043
75161
|
load_skills: tool.schema.array(tool.schema.string()).describe("Skill names to inject. REQUIRED - pass [] if no skills needed."),
|
|
75044
75162
|
description: tool.schema.string().describe("Short task description (3-5 words)"),
|
|
75045
75163
|
prompt: tool.schema.string().describe("Full detailed prompt for the agent"),
|
|
75046
|
-
run_in_background: tool.schema.boolean().describe("true=async (returns task_id), false=sync (waits).
|
|
75164
|
+
run_in_background: tool.schema.boolean().describe("REQUIRED. true=async (returns task_id), false=sync (waits). Use false for task delegation, true ONLY for parallel exploration."),
|
|
75047
75165
|
category: tool.schema.string().optional().describe(`REQUIRED if subagent_type not provided. Do NOT provide both category and subagent_type.`),
|
|
75048
75166
|
subagent_type: tool.schema.string().optional().describe("REQUIRED if category not provided. Do NOT provide both category and subagent_type."),
|
|
75049
75167
|
session_id: tool.schema.string().optional().describe("Existing Task session to continue"),
|
|
@@ -75064,11 +75182,7 @@ function createDelegateTask(options) {
|
|
|
75064
75182
|
title: args.description
|
|
75065
75183
|
});
|
|
75066
75184
|
if (args.run_in_background === undefined) {
|
|
75067
|
-
|
|
75068
|
-
args.run_in_background = false;
|
|
75069
|
-
} else {
|
|
75070
|
-
throw new Error(`Invalid arguments: 'run_in_background' parameter is REQUIRED. Use run_in_background=false for task delegation, run_in_background=true only for parallel exploration.`);
|
|
75071
|
-
}
|
|
75185
|
+
throw new Error(`Invalid arguments: 'run_in_background' parameter is REQUIRED. Specify run_in_background=false for task delegation, or run_in_background=true for parallel exploration.`);
|
|
75072
75186
|
}
|
|
75073
75187
|
if (typeof args.load_skills === "string") {
|
|
75074
75188
|
try {
|
|
@@ -75186,7 +75300,7 @@ function createDelegateTask(options) {
|
|
|
75186
75300
|
// src/tools/delegate-task/index.ts
|
|
75187
75301
|
init_constants();
|
|
75188
75302
|
// src/tools/task/task-create.ts
|
|
75189
|
-
import { join as
|
|
75303
|
+
import { join as join80 } from "path";
|
|
75190
75304
|
|
|
75191
75305
|
// src/tools/task/types.ts
|
|
75192
75306
|
var TaskStatusSchema = exports_external.enum(["pending", "in_progress", "completed", "deleted"]);
|
|
@@ -75240,18 +75354,18 @@ var TaskDeleteInputSchema = exports_external.object({
|
|
|
75240
75354
|
});
|
|
75241
75355
|
|
|
75242
75356
|
// src/features/claude-tasks/storage.ts
|
|
75243
|
-
import { join as
|
|
75244
|
-
import { existsSync as
|
|
75357
|
+
import { join as join79, dirname as dirname22, basename as basename9, isAbsolute as isAbsolute8 } from "path";
|
|
75358
|
+
import { existsSync as existsSync72, mkdirSync as mkdirSync14, readFileSync as readFileSync47, writeFileSync as writeFileSync20, renameSync as renameSync2, unlinkSync as unlinkSync12, readdirSync as readdirSync19 } from "fs";
|
|
75245
75359
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
75246
75360
|
function getTaskDir(config4 = {}) {
|
|
75247
75361
|
const tasksConfig = config4.sisyphus?.tasks;
|
|
75248
75362
|
const storagePath = tasksConfig?.storage_path;
|
|
75249
75363
|
if (storagePath) {
|
|
75250
|
-
return isAbsolute8(storagePath) ? storagePath :
|
|
75364
|
+
return isAbsolute8(storagePath) ? storagePath : join79(process.cwd(), storagePath);
|
|
75251
75365
|
}
|
|
75252
75366
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
75253
75367
|
const listId = resolveTaskListId(config4);
|
|
75254
|
-
return
|
|
75368
|
+
return join79(configDir, "tasks", listId);
|
|
75255
75369
|
}
|
|
75256
75370
|
function sanitizePathSegment(value) {
|
|
75257
75371
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-") || "default";
|
|
@@ -75269,13 +75383,13 @@ function resolveTaskListId(config4 = {}) {
|
|
|
75269
75383
|
return sanitizePathSegment(basename9(process.cwd()));
|
|
75270
75384
|
}
|
|
75271
75385
|
function ensureDir(dirPath) {
|
|
75272
|
-
if (!
|
|
75386
|
+
if (!existsSync72(dirPath)) {
|
|
75273
75387
|
mkdirSync14(dirPath, { recursive: true });
|
|
75274
75388
|
}
|
|
75275
75389
|
}
|
|
75276
75390
|
function readJsonSafe(filePath, schema2) {
|
|
75277
75391
|
try {
|
|
75278
|
-
if (!
|
|
75392
|
+
if (!existsSync72(filePath)) {
|
|
75279
75393
|
return null;
|
|
75280
75394
|
}
|
|
75281
75395
|
const content = readFileSync47(filePath, "utf-8");
|
|
@@ -75298,7 +75412,7 @@ function writeJsonAtomic(filePath, data) {
|
|
|
75298
75412
|
renameSync2(tempPath, filePath);
|
|
75299
75413
|
} catch (error92) {
|
|
75300
75414
|
try {
|
|
75301
|
-
if (
|
|
75415
|
+
if (existsSync72(tempPath)) {
|
|
75302
75416
|
unlinkSync12(tempPath);
|
|
75303
75417
|
}
|
|
75304
75418
|
} catch {}
|
|
@@ -75310,7 +75424,7 @@ function generateTaskId() {
|
|
|
75310
75424
|
return `T-${randomUUID3()}`;
|
|
75311
75425
|
}
|
|
75312
75426
|
function acquireLock(dirPath) {
|
|
75313
|
-
const lockPath =
|
|
75427
|
+
const lockPath = join79(dirPath, ".lock");
|
|
75314
75428
|
const lockId = randomUUID3();
|
|
75315
75429
|
const createLock = (timestamp2) => {
|
|
75316
75430
|
writeFileSync20(lockPath, JSON.stringify({ id: lockId, timestamp: timestamp2 }), {
|
|
@@ -75358,7 +75472,7 @@ function acquireLock(dirPath) {
|
|
|
75358
75472
|
acquired: true,
|
|
75359
75473
|
release: () => {
|
|
75360
75474
|
try {
|
|
75361
|
-
if (!
|
|
75475
|
+
if (!existsSync72(lockPath))
|
|
75362
75476
|
return;
|
|
75363
75477
|
const lockContent = readFileSync47(lockPath, "utf-8");
|
|
75364
75478
|
const lockData = JSON.parse(lockContent);
|
|
@@ -75523,7 +75637,7 @@ async function handleCreate(args, config4, ctx, context) {
|
|
|
75523
75637
|
threadID: context.sessionID
|
|
75524
75638
|
};
|
|
75525
75639
|
const validatedTask = TaskObjectSchema.parse(task);
|
|
75526
|
-
writeJsonAtomic(
|
|
75640
|
+
writeJsonAtomic(join80(taskDir, `${taskId}.json`), validatedTask);
|
|
75527
75641
|
await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
|
|
75528
75642
|
return JSON.stringify({
|
|
75529
75643
|
task: {
|
|
@@ -75545,7 +75659,7 @@ async function handleCreate(args, config4, ctx, context) {
|
|
|
75545
75659
|
}
|
|
75546
75660
|
}
|
|
75547
75661
|
// src/tools/task/task-get.ts
|
|
75548
|
-
import { join as
|
|
75662
|
+
import { join as join81 } from "path";
|
|
75549
75663
|
var TASK_ID_PATTERN = /^T-[A-Za-z0-9-]+$/;
|
|
75550
75664
|
function parseTaskId(id) {
|
|
75551
75665
|
if (!TASK_ID_PATTERN.test(id))
|
|
@@ -75570,7 +75684,7 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
75570
75684
|
return JSON.stringify({ error: "invalid_task_id" });
|
|
75571
75685
|
}
|
|
75572
75686
|
const taskDir = getTaskDir(config4);
|
|
75573
|
-
const taskPath =
|
|
75687
|
+
const taskPath = join81(taskDir, `${taskId}.json`);
|
|
75574
75688
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
75575
75689
|
return JSON.stringify({ task: task ?? null });
|
|
75576
75690
|
} catch (error92) {
|
|
@@ -75583,8 +75697,8 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
75583
75697
|
});
|
|
75584
75698
|
}
|
|
75585
75699
|
// src/tools/task/task-list.ts
|
|
75586
|
-
import { join as
|
|
75587
|
-
import { existsSync as
|
|
75700
|
+
import { join as join82 } from "path";
|
|
75701
|
+
import { existsSync as existsSync73, readdirSync as readdirSync20 } from "fs";
|
|
75588
75702
|
function createTaskList(config4) {
|
|
75589
75703
|
return tool({
|
|
75590
75704
|
description: `List all active tasks with summary information.
|
|
@@ -75595,7 +75709,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
75595
75709
|
args: {},
|
|
75596
75710
|
execute: async () => {
|
|
75597
75711
|
const taskDir = getTaskDir(config4);
|
|
75598
|
-
if (!
|
|
75712
|
+
if (!existsSync73(taskDir)) {
|
|
75599
75713
|
return JSON.stringify({ tasks: [] });
|
|
75600
75714
|
}
|
|
75601
75715
|
const files = readdirSync20(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
|
|
@@ -75604,7 +75718,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
75604
75718
|
}
|
|
75605
75719
|
const allTasks = [];
|
|
75606
75720
|
for (const fileId of files) {
|
|
75607
|
-
const task = readJsonSafe(
|
|
75721
|
+
const task = readJsonSafe(join82(taskDir, `${fileId}.json`), TaskObjectSchema);
|
|
75608
75722
|
if (task) {
|
|
75609
75723
|
allTasks.push(task);
|
|
75610
75724
|
}
|
|
@@ -75631,7 +75745,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
75631
75745
|
});
|
|
75632
75746
|
}
|
|
75633
75747
|
// src/tools/task/task-update.ts
|
|
75634
|
-
import { join as
|
|
75748
|
+
import { join as join83 } from "path";
|
|
75635
75749
|
var TASK_ID_PATTERN2 = /^T-[A-Za-z0-9-]+$/;
|
|
75636
75750
|
function parseTaskId2(id) {
|
|
75637
75751
|
if (!TASK_ID_PATTERN2.test(id))
|
|
@@ -75679,7 +75793,7 @@ async function handleUpdate(args, config4, ctx, context) {
|
|
|
75679
75793
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
75680
75794
|
}
|
|
75681
75795
|
try {
|
|
75682
|
-
const taskPath =
|
|
75796
|
+
const taskPath = join83(taskDir, `${taskId}.json`);
|
|
75683
75797
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
75684
75798
|
if (!task) {
|
|
75685
75799
|
return JSON.stringify({ error: "task_not_found" });
|
|
@@ -77098,66 +77212,92 @@ WORKFLOW:
|
|
|
77098
77212
|
4. If same file needs another call, re-read first.
|
|
77099
77213
|
5. Use anchors as "LINE#ID" only (never include trailing "|content").
|
|
77100
77214
|
|
|
77101
|
-
|
|
77102
|
-
|
|
77103
|
-
|
|
77104
|
-
|
|
77105
|
-
|
|
77106
|
-
|
|
77215
|
+
<must>
|
|
77216
|
+
- SNAPSHOT: All edits in one call reference the ORIGINAL file state. Do NOT adjust line numbers for prior edits in the same call \u2014 the system applies them bottom-up automatically.
|
|
77217
|
+
- replace removes lines pos..end (inclusive) and inserts lines in their place. Lines BEFORE pos and AFTER end are UNTOUCHED \u2014 do NOT include them in lines. If you do, they will appear twice.
|
|
77218
|
+
- lines must contain ONLY the content that belongs inside the consumed range. Content after end survives unchanged.
|
|
77219
|
+
- Tags MUST be copied exactly from read output or >>> mismatch output. NEVER guess tags.
|
|
77220
|
+
- Batch = multiple operations in edits[], NOT one big replace covering everything. Each operation targets the smallest possible change.
|
|
77221
|
+
- lines must contain plain replacement text only (no LINE#ID prefixes, no diff + markers).
|
|
77222
|
+
</must>
|
|
77223
|
+
|
|
77224
|
+
<operations>
|
|
77225
|
+
LINE#ID FORMAT:
|
|
77226
|
+
Each line reference must be in "{line_number}#{hash_id}" format where:
|
|
77227
|
+
{line_number}: 1-based line number
|
|
77228
|
+
{hash_id}: Two CID letters from the set ZPMQVRWSNKTXJBYH
|
|
77107
77229
|
|
|
77108
|
-
|
|
77109
|
-
|
|
77110
|
-
|
|
77111
|
-
|
|
77112
|
-
|
|
77113
|
-
|
|
77114
|
-
delete=true deletes file and requires edits=[] with no rename
|
|
77115
|
-
rename moves final content to a new path and removes old path
|
|
77230
|
+
OPERATION CHOICE:
|
|
77231
|
+
replace with pos only -> replace one line at pos
|
|
77232
|
+
replace with pos+end -> replace range pos..end inclusive as a block (ranges MUST NOT overlap across edits)
|
|
77233
|
+
append with pos/end anchor -> insert after that anchor
|
|
77234
|
+
prepend with pos/end anchor -> insert before that anchor
|
|
77235
|
+
append/prepend without anchors -> EOF/BOF insertion (also creates missing files)
|
|
77116
77236
|
|
|
77117
77237
|
CONTENT FORMAT:
|
|
77118
77238
|
lines can be a string (single line) or string[] (multi-line, preferred).
|
|
77119
77239
|
If you pass a multi-line string, it is split by real newline characters.
|
|
77120
|
-
|
|
77240
|
+
lines: null or lines: [] with replace -> delete those lines.
|
|
77121
77241
|
|
|
77122
|
-
FILE
|
|
77123
|
-
|
|
77124
|
-
|
|
77125
|
-
CRITICAL: only unanchored append/prepend can create a missing file.
|
|
77242
|
+
FILE MODES:
|
|
77243
|
+
delete=true deletes file and requires edits=[] with no rename
|
|
77244
|
+
rename moves final content to a new path and removes old path
|
|
77126
77245
|
|
|
77127
|
-
|
|
77128
|
-
|
|
77129
|
-
|
|
77130
|
-
|
|
77131
|
-
|
|
77132
|
-
|
|
77133
|
-
|
|
77134
|
-
|
|
77135
|
-
|
|
77136
|
-
|
|
77137
|
-
|
|
77138
|
-
|
|
77139
|
-
|
|
77140
|
-
|
|
77141
|
-
|
|
77142
|
-
|
|
77143
|
-
|
|
77144
|
-
|
|
77145
|
-
|
|
77146
|
-
|
|
77147
|
-
|
|
77148
|
-
|
|
77149
|
-
|
|
77150
|
-
|
|
77151
|
-
|
|
77152
|
-
|
|
77153
|
-
|
|
77154
|
-
|
|
77155
|
-
|
|
77246
|
+
RULES:
|
|
77247
|
+
1. Minimize scope: one logical mutation site per operation.
|
|
77248
|
+
2. Preserve formatting: keep indentation, punctuation, line breaks, trailing commas, brace style.
|
|
77249
|
+
3. Prefer insertion over neighbor rewrites: anchor to structural boundaries (}, ], },), not interior property lines.
|
|
77250
|
+
4. No no-ops: replacement content must differ from current content.
|
|
77251
|
+
5. Touch only requested code: avoid incidental edits.
|
|
77252
|
+
6. Use exact current tokens: NEVER rewrite approximately.
|
|
77253
|
+
7. For swaps/moves: prefer one range operation over multiple single-line operations.
|
|
77254
|
+
8. Anchor to structural lines (function/class/brace), NEVER blank lines.
|
|
77255
|
+
9. Re-read after each successful edit call before issuing another on the same file.
|
|
77256
|
+
</operations>
|
|
77257
|
+
|
|
77258
|
+
<examples>
|
|
77259
|
+
Given this file content after read:
|
|
77260
|
+
10#VK|function hello() {
|
|
77261
|
+
11#XJ| console.log("hi");
|
|
77262
|
+
12#MB| console.log("bye");
|
|
77263
|
+
13#QR|}
|
|
77264
|
+
14#TN|
|
|
77265
|
+
15#WS|function world() {
|
|
77266
|
+
|
|
77267
|
+
Single-line replace (change line 11):
|
|
77268
|
+
{ op: "replace", pos: "11#XJ", lines: [" console.log(\\"hello\\");"] }
|
|
77269
|
+
Result: line 11 replaced. Lines 10, 12-15 unchanged.
|
|
77270
|
+
|
|
77271
|
+
Range replace (rewrite function body, lines 11-12):
|
|
77272
|
+
{ op: "replace", pos: "11#XJ", end: "12#MB", lines: [" return \\"hello world\\";"] }
|
|
77273
|
+
Result: lines 11-12 removed, replaced by 1 new line. Lines 10, 13-15 unchanged.
|
|
77274
|
+
|
|
77275
|
+
Delete a line:
|
|
77276
|
+
{ op: "replace", pos: "12#MB", lines: null }
|
|
77277
|
+
Result: line 12 removed. Lines 10-11, 13-15 unchanged.
|
|
77278
|
+
|
|
77279
|
+
Insert after line 13 (between functions):
|
|
77280
|
+
{ op: "append", pos: "13#QR", lines: ["", "function added() {", " return true;", "}"] }
|
|
77281
|
+
Result: 4 new lines inserted after line 13. All existing lines unchanged.
|
|
77282
|
+
|
|
77283
|
+
BAD \u2014 lines extend past end (DUPLICATES line 13):
|
|
77284
|
+
{ op: "replace", pos: "11#XJ", end: "12#MB", lines: [" return \\"hi\\";", "}"] }
|
|
77285
|
+
Line 13 is "}" which already exists after end. Including "}" in lines duplicates it.
|
|
77286
|
+
CORRECT: { op: "replace", pos: "11#XJ", end: "12#MB", lines: [" return \\"hi\\";"] }
|
|
77287
|
+
</examples>
|
|
77288
|
+
|
|
77289
|
+
<auto>
|
|
77290
|
+
Built-in autocorrect (you do NOT need to handle these):
|
|
77291
|
+
Merged lines are auto-expanded back to original line count.
|
|
77292
|
+
Indentation is auto-restored from original lines.
|
|
77293
|
+
BOM and CRLF line endings are preserved automatically.
|
|
77294
|
+
Hashline prefixes and diff markers in text are auto-stripped.
|
|
77295
|
+
Boundary echo lines (duplicating adjacent surviving lines) are auto-stripped.
|
|
77296
|
+
</auto>
|
|
77156
77297
|
|
|
77157
77298
|
RECOVERY (when >>> mismatch error appears):
|
|
77158
|
-
|
|
77159
|
-
|
|
77160
|
-
ALWAYS batch all edits for one file in a single call.`;
|
|
77299
|
+
Copy the updated LINE#ID tags shown in the error output directly.
|
|
77300
|
+
Re-read only if the needed tags are missing from the error snippet.`;
|
|
77161
77301
|
|
|
77162
77302
|
// src/tools/hashline-edit/tools.ts
|
|
77163
77303
|
function createHashlineEditTool() {
|
|
@@ -77175,7 +77315,7 @@ function createHashlineEditTool() {
|
|
|
77175
77315
|
]).describe("Hashline edit operation mode"),
|
|
77176
77316
|
pos: tool.schema.string().optional().describe("Primary anchor in LINE#ID format"),
|
|
77177
77317
|
end: tool.schema.string().optional().describe("Range end anchor in LINE#ID format"),
|
|
77178
|
-
lines: tool.schema.union([tool.schema.string(), tool.schema.null()]).describe("Replacement or inserted lines as newline-delimited string. null deletes with replace")
|
|
77318
|
+
lines: tool.schema.union([tool.schema.array(tool.schema.string()), tool.schema.string(), tool.schema.null()]).describe("Replacement or inserted lines as newline-delimited string. null deletes with replace")
|
|
77179
77319
|
})).describe("Array of edit operations to apply (empty when delete=true)")
|
|
77180
77320
|
},
|
|
77181
77321
|
execute: async (args, context) => executeHashlineEditTool(args, context)
|
|
@@ -77886,9 +78026,13 @@ class ConcurrencyManager {
|
|
|
77886
78026
|
|
|
77887
78027
|
// src/features/background-agent/constants.ts
|
|
77888
78028
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
78029
|
+
var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
|
|
77889
78030
|
var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
77890
|
-
var DEFAULT_STALE_TIMEOUT_MS =
|
|
78031
|
+
var DEFAULT_STALE_TIMEOUT_MS = 1200000;
|
|
77891
78032
|
var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
|
|
78033
|
+
var DEFAULT_MAX_TOOL_CALLS = 200;
|
|
78034
|
+
var DEFAULT_CIRCUIT_BREAKER_WINDOW_SIZE = 20;
|
|
78035
|
+
var DEFAULT_CIRCUIT_BREAKER_REPETITION_THRESHOLD_PERCENT = 80;
|
|
77892
78036
|
var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
|
|
77893
78037
|
var MIN_IDLE_TIME_MS = 5000;
|
|
77894
78038
|
var POLLING_INTERVAL_MS = 3000;
|
|
@@ -78134,7 +78278,7 @@ function unregisterManagerForCleanup(manager) {
|
|
|
78134
78278
|
|
|
78135
78279
|
// src/features/background-agent/compaction-aware-message-resolver.ts
|
|
78136
78280
|
import { readdirSync as readdirSync21, readFileSync as readFileSync48 } from "fs";
|
|
78137
|
-
import { join as
|
|
78281
|
+
import { join as join84 } from "path";
|
|
78138
78282
|
function isCompactionAgent4(agent) {
|
|
78139
78283
|
return agent?.trim().toLowerCase() === "compaction";
|
|
78140
78284
|
}
|
|
@@ -78213,7 +78357,7 @@ function findNearestMessageExcludingCompaction(messageDir, sessionID) {
|
|
|
78213
78357
|
const messages = [];
|
|
78214
78358
|
for (const file3 of files) {
|
|
78215
78359
|
try {
|
|
78216
|
-
const content = readFileSync48(
|
|
78360
|
+
const content = readFileSync48(join84(messageDir, file3), "utf-8");
|
|
78217
78361
|
messages.push(JSON.parse(content));
|
|
78218
78362
|
} catch {
|
|
78219
78363
|
continue;
|
|
@@ -78299,7 +78443,7 @@ function handleSessionIdleBackgroundEvent(args) {
|
|
|
78299
78443
|
}
|
|
78300
78444
|
|
|
78301
78445
|
// src/features/background-agent/manager.ts
|
|
78302
|
-
import { join as
|
|
78446
|
+
import { join as join85 } from "path";
|
|
78303
78447
|
|
|
78304
78448
|
// src/features/background-agent/remove-task-toast-tracking.ts
|
|
78305
78449
|
function removeTaskToastTracking(taskId) {
|
|
@@ -78310,7 +78454,6 @@ function removeTaskToastTracking(taskId) {
|
|
|
78310
78454
|
}
|
|
78311
78455
|
|
|
78312
78456
|
// src/features/background-agent/task-poller.ts
|
|
78313
|
-
var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
|
|
78314
78457
|
var TERMINAL_TASK_STATUSES = new Set([
|
|
78315
78458
|
"completed",
|
|
78316
78459
|
"error",
|
|
@@ -78441,6 +78584,57 @@ async function checkAndInterruptStaleTasks(args) {
|
|
|
78441
78584
|
}
|
|
78442
78585
|
}
|
|
78443
78586
|
|
|
78587
|
+
// src/features/background-agent/loop-detector.ts
|
|
78588
|
+
function resolveCircuitBreakerSettings(config4) {
|
|
78589
|
+
return {
|
|
78590
|
+
maxToolCalls: config4?.circuitBreaker?.maxToolCalls ?? config4?.maxToolCalls ?? DEFAULT_MAX_TOOL_CALLS,
|
|
78591
|
+
windowSize: config4?.circuitBreaker?.windowSize ?? DEFAULT_CIRCUIT_BREAKER_WINDOW_SIZE,
|
|
78592
|
+
repetitionThresholdPercent: config4?.circuitBreaker?.repetitionThresholdPercent ?? DEFAULT_CIRCUIT_BREAKER_REPETITION_THRESHOLD_PERCENT
|
|
78593
|
+
};
|
|
78594
|
+
}
|
|
78595
|
+
function recordToolCall(window, toolName, settings) {
|
|
78596
|
+
const previous = window?.toolNames ?? [];
|
|
78597
|
+
const toolNames = [...previous, toolName].slice(-settings.windowSize);
|
|
78598
|
+
return {
|
|
78599
|
+
toolNames,
|
|
78600
|
+
windowSize: settings.windowSize,
|
|
78601
|
+
thresholdPercent: settings.repetitionThresholdPercent
|
|
78602
|
+
};
|
|
78603
|
+
}
|
|
78604
|
+
function detectRepetitiveToolUse(window) {
|
|
78605
|
+
if (!window || window.toolNames.length === 0) {
|
|
78606
|
+
return { triggered: false };
|
|
78607
|
+
}
|
|
78608
|
+
const counts = new Map;
|
|
78609
|
+
for (const toolName of window.toolNames) {
|
|
78610
|
+
counts.set(toolName, (counts.get(toolName) ?? 0) + 1);
|
|
78611
|
+
}
|
|
78612
|
+
let repeatedTool;
|
|
78613
|
+
let repeatedCount = 0;
|
|
78614
|
+
for (const [toolName, count] of counts.entries()) {
|
|
78615
|
+
if (count > repeatedCount) {
|
|
78616
|
+
repeatedTool = toolName;
|
|
78617
|
+
repeatedCount = count;
|
|
78618
|
+
}
|
|
78619
|
+
}
|
|
78620
|
+
const sampleSize = window.toolNames.length;
|
|
78621
|
+
const minimumSampleSize = Math.min(window.windowSize, Math.ceil(window.windowSize * window.thresholdPercent / 100));
|
|
78622
|
+
if (sampleSize < minimumSampleSize) {
|
|
78623
|
+
return { triggered: false };
|
|
78624
|
+
}
|
|
78625
|
+
const thresholdCount = Math.ceil(sampleSize * window.thresholdPercent / 100);
|
|
78626
|
+
if (!repeatedTool || repeatedCount < thresholdCount) {
|
|
78627
|
+
return { triggered: false };
|
|
78628
|
+
}
|
|
78629
|
+
return {
|
|
78630
|
+
triggered: true,
|
|
78631
|
+
toolName: repeatedTool,
|
|
78632
|
+
repeatedCount,
|
|
78633
|
+
sampleSize,
|
|
78634
|
+
thresholdPercent: window.thresholdPercent
|
|
78635
|
+
};
|
|
78636
|
+
}
|
|
78637
|
+
|
|
78444
78638
|
// src/features/background-agent/subagent-spawn-limits.ts
|
|
78445
78639
|
var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
78446
78640
|
var DEFAULT_MAX_ROOT_SESSION_SPAWN_BUDGET = 50;
|
|
@@ -78499,6 +78693,18 @@ function createSubagentDescendantLimitError(input) {
|
|
|
78499
78693
|
}
|
|
78500
78694
|
|
|
78501
78695
|
// src/features/background-agent/manager.ts
|
|
78696
|
+
function resolveMessagePartInfo(properties) {
|
|
78697
|
+
if (!properties || typeof properties !== "object") {
|
|
78698
|
+
return;
|
|
78699
|
+
}
|
|
78700
|
+
const nestedPart = properties.part;
|
|
78701
|
+
if (nestedPart && typeof nestedPart === "object") {
|
|
78702
|
+
return nestedPart;
|
|
78703
|
+
}
|
|
78704
|
+
return properties;
|
|
78705
|
+
}
|
|
78706
|
+
var MAX_TASK_REMOVAL_RESCHEDULES = 6;
|
|
78707
|
+
|
|
78502
78708
|
class BackgroundManager {
|
|
78503
78709
|
tasks;
|
|
78504
78710
|
notifications;
|
|
@@ -78987,6 +79193,8 @@ class BackgroundManager {
|
|
|
78987
79193
|
existingTask.startedAt = new Date;
|
|
78988
79194
|
existingTask.progress = {
|
|
78989
79195
|
toolCalls: existingTask.progress?.toolCalls ?? 0,
|
|
79196
|
+
toolCallWindow: existingTask.progress?.toolCallWindow,
|
|
79197
|
+
countedToolPartIDs: existingTask.progress?.countedToolPartIDs,
|
|
78990
79198
|
lastUpdate: new Date
|
|
78991
79199
|
};
|
|
78992
79200
|
this.startPolling();
|
|
@@ -79094,9 +79302,7 @@ class BackgroundManager {
|
|
|
79094
79302
|
this.tryFallbackRetry(task, errorInfo, "message.updated");
|
|
79095
79303
|
}
|
|
79096
79304
|
if (event.type === "message.part.updated" || event.type === "message.part.delta") {
|
|
79097
|
-
|
|
79098
|
-
return;
|
|
79099
|
-
const partInfo = props;
|
|
79305
|
+
const partInfo = resolveMessagePartInfo(props);
|
|
79100
79306
|
const sessionID = partInfo?.sessionID;
|
|
79101
79307
|
if (!sessionID)
|
|
79102
79308
|
return;
|
|
@@ -79116,8 +79322,51 @@ class BackgroundManager {
|
|
|
79116
79322
|
}
|
|
79117
79323
|
task.progress.lastUpdate = new Date;
|
|
79118
79324
|
if (partInfo?.type === "tool" || partInfo?.tool) {
|
|
79325
|
+
const countedToolPartIDs = task.progress.countedToolPartIDs ?? [];
|
|
79326
|
+
const shouldCountToolCall = !partInfo.id || partInfo.state?.status !== "running" || !countedToolPartIDs.includes(partInfo.id);
|
|
79327
|
+
if (!shouldCountToolCall) {
|
|
79328
|
+
return;
|
|
79329
|
+
}
|
|
79330
|
+
if (partInfo.id && partInfo.state?.status === "running") {
|
|
79331
|
+
task.progress.countedToolPartIDs = [...countedToolPartIDs, partInfo.id];
|
|
79332
|
+
}
|
|
79119
79333
|
task.progress.toolCalls += 1;
|
|
79120
79334
|
task.progress.lastTool = partInfo.tool;
|
|
79335
|
+
const circuitBreaker = resolveCircuitBreakerSettings(this.config);
|
|
79336
|
+
if (partInfo.tool) {
|
|
79337
|
+
task.progress.toolCallWindow = recordToolCall(task.progress.toolCallWindow, partInfo.tool, circuitBreaker);
|
|
79338
|
+
const loopDetection = detectRepetitiveToolUse(task.progress.toolCallWindow);
|
|
79339
|
+
if (loopDetection.triggered) {
|
|
79340
|
+
log("[background-agent] Circuit breaker: repetitive tool usage detected", {
|
|
79341
|
+
taskId: task.id,
|
|
79342
|
+
agent: task.agent,
|
|
79343
|
+
sessionID,
|
|
79344
|
+
toolName: loopDetection.toolName,
|
|
79345
|
+
repeatedCount: loopDetection.repeatedCount,
|
|
79346
|
+
sampleSize: loopDetection.sampleSize,
|
|
79347
|
+
thresholdPercent: loopDetection.thresholdPercent
|
|
79348
|
+
});
|
|
79349
|
+
this.cancelTask(task.id, {
|
|
79350
|
+
source: "circuit-breaker",
|
|
79351
|
+
reason: `Subagent repeatedly called ${loopDetection.toolName} ${loopDetection.repeatedCount}/${loopDetection.sampleSize} times in the recent tool-call window (${loopDetection.thresholdPercent}% threshold). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`
|
|
79352
|
+
});
|
|
79353
|
+
return;
|
|
79354
|
+
}
|
|
79355
|
+
}
|
|
79356
|
+
const maxToolCalls = circuitBreaker.maxToolCalls;
|
|
79357
|
+
if (task.progress.toolCalls >= maxToolCalls) {
|
|
79358
|
+
log("[background-agent] Circuit breaker: tool call limit reached", {
|
|
79359
|
+
taskId: task.id,
|
|
79360
|
+
toolCalls: task.progress.toolCalls,
|
|
79361
|
+
maxToolCalls,
|
|
79362
|
+
agent: task.agent,
|
|
79363
|
+
sessionID
|
|
79364
|
+
});
|
|
79365
|
+
this.cancelTask(task.id, {
|
|
79366
|
+
source: "circuit-breaker",
|
|
79367
|
+
reason: `Subagent exceeded maximum tool call limit (${maxToolCalls}). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`
|
|
79368
|
+
});
|
|
79369
|
+
}
|
|
79121
79370
|
}
|
|
79122
79371
|
}
|
|
79123
79372
|
if (event.type === "session.idle") {
|
|
@@ -79362,7 +79611,7 @@ ${originalText}`;
|
|
|
79362
79611
|
this.taskHistory.clearSession(parentSessionID);
|
|
79363
79612
|
this.completedTaskSummaries.delete(parentSessionID);
|
|
79364
79613
|
}
|
|
79365
|
-
scheduleTaskRemoval(taskId) {
|
|
79614
|
+
scheduleTaskRemoval(taskId, rescheduleCount = 0) {
|
|
79366
79615
|
const existingTimer = this.completionTimers.get(taskId);
|
|
79367
79616
|
if (existingTimer) {
|
|
79368
79617
|
clearTimeout(existingTimer);
|
|
@@ -79371,17 +79620,26 @@ ${originalText}`;
|
|
|
79371
79620
|
const timer = setTimeout(() => {
|
|
79372
79621
|
this.completionTimers.delete(taskId);
|
|
79373
79622
|
const task = this.tasks.get(taskId);
|
|
79374
|
-
if (task)
|
|
79375
|
-
|
|
79376
|
-
|
|
79377
|
-
this.
|
|
79378
|
-
|
|
79379
|
-
|
|
79380
|
-
|
|
79623
|
+
if (!task)
|
|
79624
|
+
return;
|
|
79625
|
+
if (task.parentSessionID) {
|
|
79626
|
+
const siblings = this.getTasksByParentSession(task.parentSessionID);
|
|
79627
|
+
const runningOrPendingSiblings = siblings.filter((sibling) => sibling.id !== taskId && (sibling.status === "running" || sibling.status === "pending"));
|
|
79628
|
+
const completedAtTimestamp = task.completedAt?.getTime();
|
|
79629
|
+
const reachedTaskTtl = completedAtTimestamp !== undefined && Date.now() - completedAtTimestamp >= TASK_TTL_MS;
|
|
79630
|
+
if (runningOrPendingSiblings.length > 0 && rescheduleCount < MAX_TASK_REMOVAL_RESCHEDULES && !reachedTaskTtl) {
|
|
79631
|
+
this.scheduleTaskRemoval(taskId, rescheduleCount + 1);
|
|
79632
|
+
return;
|
|
79381
79633
|
}
|
|
79382
|
-
log("[background-agent] Removed completed task from memory:", taskId);
|
|
79383
|
-
this.clearTaskHistoryWhenParentTasksGone(task?.parentSessionID);
|
|
79384
79634
|
}
|
|
79635
|
+
this.clearNotificationsForTask(taskId);
|
|
79636
|
+
this.tasks.delete(taskId);
|
|
79637
|
+
this.clearTaskHistoryWhenParentTasksGone(task.parentSessionID);
|
|
79638
|
+
if (task.sessionID) {
|
|
79639
|
+
subagentSessions.delete(task.sessionID);
|
|
79640
|
+
SessionCategoryRegistry.remove(task.sessionID);
|
|
79641
|
+
}
|
|
79642
|
+
log("[background-agent] Removed completed task from memory:", taskId);
|
|
79385
79643
|
}, TASK_CLEANUP_DELAY_MS);
|
|
79386
79644
|
this.completionTimers.set(taskId, timer);
|
|
79387
79645
|
}
|
|
@@ -79602,7 +79860,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
79602
79860
|
parentSessionID: task.parentSessionID
|
|
79603
79861
|
});
|
|
79604
79862
|
}
|
|
79605
|
-
const messageDir =
|
|
79863
|
+
const messageDir = join85(MESSAGE_STORAGE, task.parentSessionID);
|
|
79606
79864
|
const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir, task.parentSessionID) : null;
|
|
79607
79865
|
agent = currentMessage?.agent ?? task.parentAgent;
|
|
79608
79866
|
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
|
|
@@ -83708,11 +83966,11 @@ class StreamableHTTPClientTransport {
|
|
|
83708
83966
|
}
|
|
83709
83967
|
|
|
83710
83968
|
// src/features/mcp-oauth/storage.ts
|
|
83711
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
83712
|
-
import { dirname as dirname23, join as
|
|
83969
|
+
import { chmodSync as chmodSync2, existsSync as existsSync74, mkdirSync as mkdirSync15, readFileSync as readFileSync49, unlinkSync as unlinkSync13, writeFileSync as writeFileSync21 } from "fs";
|
|
83970
|
+
import { dirname as dirname23, join as join86 } from "path";
|
|
83713
83971
|
var STORAGE_FILE_NAME = "mcp-oauth.json";
|
|
83714
83972
|
function getMcpOauthStoragePath() {
|
|
83715
|
-
return
|
|
83973
|
+
return join86(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
|
|
83716
83974
|
}
|
|
83717
83975
|
function normalizeHost(serverHost) {
|
|
83718
83976
|
let host = serverHost.trim();
|
|
@@ -83749,7 +84007,7 @@ function buildKey(serverHost, resource) {
|
|
|
83749
84007
|
}
|
|
83750
84008
|
function readStore() {
|
|
83751
84009
|
const filePath = getMcpOauthStoragePath();
|
|
83752
|
-
if (!
|
|
84010
|
+
if (!existsSync74(filePath)) {
|
|
83753
84011
|
return null;
|
|
83754
84012
|
}
|
|
83755
84013
|
try {
|
|
@@ -83763,7 +84021,7 @@ function writeStore(store2) {
|
|
|
83763
84021
|
const filePath = getMcpOauthStoragePath();
|
|
83764
84022
|
try {
|
|
83765
84023
|
const dir = dirname23(filePath);
|
|
83766
|
-
if (!
|
|
84024
|
+
if (!existsSync74(dir)) {
|
|
83767
84025
|
mkdirSync15(dir, { recursive: true });
|
|
83768
84026
|
}
|
|
83769
84027
|
writeFileSync21(filePath, JSON.stringify(store2, null, 2), { encoding: "utf-8", mode: 384 });
|
|
@@ -84565,7 +84823,19 @@ var EXCLUDED_ENV_PATTERNS = [
|
|
|
84565
84823
|
/^npm_config_/,
|
|
84566
84824
|
/^YARN_/,
|
|
84567
84825
|
/^PNPM_/,
|
|
84568
|
-
/^NO_UPDATE_NOTIFIER
|
|
84826
|
+
/^NO_UPDATE_NOTIFIER$/,
|
|
84827
|
+
/^ANTHROPIC_API_KEY$/i,
|
|
84828
|
+
/^AWS_ACCESS_KEY_ID$/i,
|
|
84829
|
+
/^AWS_SECRET_ACCESS_KEY$/i,
|
|
84830
|
+
/^GITHUB_TOKEN$/i,
|
|
84831
|
+
/^DATABASE_URL$/i,
|
|
84832
|
+
/^OPENAI_API_KEY$/i,
|
|
84833
|
+
/_KEY$/i,
|
|
84834
|
+
/_SECRET$/i,
|
|
84835
|
+
/_TOKEN$/i,
|
|
84836
|
+
/_PASSWORD$/i,
|
|
84837
|
+
/_CREDENTIAL$/i,
|
|
84838
|
+
/_API_KEY$/i
|
|
84569
84839
|
];
|
|
84570
84840
|
function createCleanMcpEnvironment(customEnv = {}) {
|
|
84571
84841
|
const cleanEnv = {};
|
|
@@ -91652,7 +91922,7 @@ function createHephaestusAgent2(model, availableAgents, availableToolNames, avai
|
|
|
91652
91922
|
}
|
|
91653
91923
|
createHephaestusAgent2.mode = MODE10;
|
|
91654
91924
|
// src/agents/builtin-agents/resolve-file-uri.ts
|
|
91655
|
-
import { existsSync as
|
|
91925
|
+
import { existsSync as existsSync75, readFileSync as readFileSync50 } from "fs";
|
|
91656
91926
|
import { homedir as homedir14 } from "os";
|
|
91657
91927
|
import { isAbsolute as isAbsolute9, resolve as resolve15 } from "path";
|
|
91658
91928
|
function resolvePromptAppend(promptAppend, configDir) {
|
|
@@ -91667,7 +91937,7 @@ function resolvePromptAppend(promptAppend, configDir) {
|
|
|
91667
91937
|
} catch {
|
|
91668
91938
|
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
|
|
91669
91939
|
}
|
|
91670
|
-
if (!
|
|
91940
|
+
if (!existsSync75(filePath)) {
|
|
91671
91941
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
|
|
91672
91942
|
}
|
|
91673
91943
|
try {
|
|
@@ -92954,8 +93224,8 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
92954
93224
|
return result;
|
|
92955
93225
|
}
|
|
92956
93226
|
// src/features/claude-code-agent-loader/loader.ts
|
|
92957
|
-
import { existsSync as
|
|
92958
|
-
import { join as
|
|
93227
|
+
import { existsSync as existsSync76, readdirSync as readdirSync22, readFileSync as readFileSync51 } from "fs";
|
|
93228
|
+
import { join as join87, basename as basename10 } from "path";
|
|
92959
93229
|
function parseToolsConfig2(toolsStr) {
|
|
92960
93230
|
if (!toolsStr)
|
|
92961
93231
|
return;
|
|
@@ -92969,7 +93239,7 @@ function parseToolsConfig2(toolsStr) {
|
|
|
92969
93239
|
return result;
|
|
92970
93240
|
}
|
|
92971
93241
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
92972
|
-
if (!
|
|
93242
|
+
if (!existsSync76(agentsDir)) {
|
|
92973
93243
|
return [];
|
|
92974
93244
|
}
|
|
92975
93245
|
const entries = readdirSync22(agentsDir, { withFileTypes: true });
|
|
@@ -92977,7 +93247,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
92977
93247
|
for (const entry of entries) {
|
|
92978
93248
|
if (!isMarkdownFile(entry))
|
|
92979
93249
|
continue;
|
|
92980
|
-
const agentPath =
|
|
93250
|
+
const agentPath = join87(agentsDir, entry.name);
|
|
92981
93251
|
const agentName = basename10(entry.name, ".md");
|
|
92982
93252
|
try {
|
|
92983
93253
|
const content = readFileSync51(agentPath, "utf-8");
|
|
@@ -93010,7 +93280,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
93010
93280
|
return agents;
|
|
93011
93281
|
}
|
|
93012
93282
|
function loadUserAgents() {
|
|
93013
|
-
const userAgentsDir =
|
|
93283
|
+
const userAgentsDir = join87(getClaudeConfigDir(), "agents");
|
|
93014
93284
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
93015
93285
|
const result = {};
|
|
93016
93286
|
for (const agent of agents) {
|
|
@@ -93019,7 +93289,7 @@ function loadUserAgents() {
|
|
|
93019
93289
|
return result;
|
|
93020
93290
|
}
|
|
93021
93291
|
function loadProjectAgents(directory) {
|
|
93022
|
-
const projectAgentsDir =
|
|
93292
|
+
const projectAgentsDir = join87(directory ?? process.cwd(), ".claude", "agents");
|
|
93023
93293
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
93024
93294
|
const result = {};
|
|
93025
93295
|
for (const agent of agents) {
|
|
@@ -95471,7 +95741,7 @@ async function applyAgentConfig(params) {
|
|
|
95471
95741
|
}
|
|
95472
95742
|
// src/features/claude-code-command-loader/loader.ts
|
|
95473
95743
|
import { promises as fs19 } from "fs";
|
|
95474
|
-
import { join as
|
|
95744
|
+
import { join as join88, basename as basename11 } from "path";
|
|
95475
95745
|
init_logger();
|
|
95476
95746
|
async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix = "") {
|
|
95477
95747
|
try {
|
|
@@ -95502,7 +95772,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
|
|
|
95502
95772
|
if (entry.isDirectory()) {
|
|
95503
95773
|
if (entry.name.startsWith("."))
|
|
95504
95774
|
continue;
|
|
95505
|
-
const subDirPath =
|
|
95775
|
+
const subDirPath = join88(commandsDir, entry.name);
|
|
95506
95776
|
const subPrefix = prefix ? `${prefix}:${entry.name}` : entry.name;
|
|
95507
95777
|
const subCommands = await loadCommandsFromDir(subDirPath, scope, visited, subPrefix);
|
|
95508
95778
|
commands3.push(...subCommands);
|
|
@@ -95510,7 +95780,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
|
|
|
95510
95780
|
}
|
|
95511
95781
|
if (!isMarkdownFile(entry))
|
|
95512
95782
|
continue;
|
|
95513
|
-
const commandPath =
|
|
95783
|
+
const commandPath = join88(commandsDir, entry.name);
|
|
95514
95784
|
const baseCommandName = basename11(entry.name, ".md");
|
|
95515
95785
|
const commandName = prefix ? `${prefix}:${baseCommandName}` : baseCommandName;
|
|
95516
95786
|
try {
|
|
@@ -95557,23 +95827,23 @@ function commandsToRecord(commands3) {
|
|
|
95557
95827
|
return result;
|
|
95558
95828
|
}
|
|
95559
95829
|
async function loadUserCommands() {
|
|
95560
|
-
const userCommandsDir =
|
|
95830
|
+
const userCommandsDir = join88(getClaudeConfigDir(), "commands");
|
|
95561
95831
|
const commands3 = await loadCommandsFromDir(userCommandsDir, "user");
|
|
95562
95832
|
return commandsToRecord(commands3);
|
|
95563
95833
|
}
|
|
95564
95834
|
async function loadProjectCommands(directory) {
|
|
95565
|
-
const projectCommandsDir =
|
|
95835
|
+
const projectCommandsDir = join88(directory ?? process.cwd(), ".claude", "commands");
|
|
95566
95836
|
const commands3 = await loadCommandsFromDir(projectCommandsDir, "project");
|
|
95567
95837
|
return commandsToRecord(commands3);
|
|
95568
95838
|
}
|
|
95569
95839
|
async function loadOpencodeGlobalCommands() {
|
|
95570
95840
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
95571
|
-
const opencodeCommandsDir =
|
|
95841
|
+
const opencodeCommandsDir = join88(configDir, "command");
|
|
95572
95842
|
const commands3 = await loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
95573
95843
|
return commandsToRecord(commands3);
|
|
95574
95844
|
}
|
|
95575
95845
|
async function loadOpencodeProjectCommands(directory) {
|
|
95576
|
-
const opencodeProjectDir =
|
|
95846
|
+
const opencodeProjectDir = join88(directory ?? process.cwd(), ".opencode", "command");
|
|
95577
95847
|
const commands3 = await loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
95578
95848
|
return commandsToRecord(commands3);
|
|
95579
95849
|
}
|
|
@@ -95632,22 +95902,22 @@ function remapCommandAgentFields(commands3) {
|
|
|
95632
95902
|
}
|
|
95633
95903
|
}
|
|
95634
95904
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
95635
|
-
import { existsSync as
|
|
95636
|
-
import { join as
|
|
95905
|
+
import { existsSync as existsSync77, readFileSync as readFileSync52 } from "fs";
|
|
95906
|
+
import { join as join89 } from "path";
|
|
95637
95907
|
import { homedir as homedir15 } from "os";
|
|
95638
95908
|
init_logger();
|
|
95639
95909
|
function getMcpConfigPaths() {
|
|
95640
95910
|
const claudeConfigDir = getClaudeConfigDir();
|
|
95641
95911
|
const cwd = process.cwd();
|
|
95642
95912
|
return [
|
|
95643
|
-
{ path:
|
|
95644
|
-
{ path:
|
|
95645
|
-
{ path:
|
|
95646
|
-
{ path:
|
|
95913
|
+
{ path: join89(homedir15(), ".claude.json"), scope: "user" },
|
|
95914
|
+
{ path: join89(claudeConfigDir, ".mcp.json"), scope: "user" },
|
|
95915
|
+
{ path: join89(cwd, ".mcp.json"), scope: "project" },
|
|
95916
|
+
{ path: join89(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
95647
95917
|
];
|
|
95648
95918
|
}
|
|
95649
95919
|
async function loadMcpConfigFile(filePath) {
|
|
95650
|
-
if (!
|
|
95920
|
+
if (!existsSync77(filePath)) {
|
|
95651
95921
|
return null;
|
|
95652
95922
|
}
|
|
95653
95923
|
try {
|
|
@@ -95662,7 +95932,7 @@ function getSystemMcpServerNames() {
|
|
|
95662
95932
|
const names = new Set;
|
|
95663
95933
|
const paths = getMcpConfigPaths();
|
|
95664
95934
|
for (const { path: path12 } of paths) {
|
|
95665
|
-
if (!
|
|
95935
|
+
if (!existsSync77(path12))
|
|
95666
95936
|
continue;
|
|
95667
95937
|
try {
|
|
95668
95938
|
const content = readFileSync52(path12, "utf-8");
|
|
@@ -96480,10 +96750,10 @@ function createChatHeadersHandler(args) {
|
|
|
96480
96750
|
|
|
96481
96751
|
// src/plugin/ultrawork-db-model-override.ts
|
|
96482
96752
|
import { Database } from "bun:sqlite";
|
|
96483
|
-
import { join as
|
|
96484
|
-
import { existsSync as
|
|
96753
|
+
import { join as join90 } from "path";
|
|
96754
|
+
import { existsSync as existsSync78 } from "fs";
|
|
96485
96755
|
function getDbPath() {
|
|
96486
|
-
return
|
|
96756
|
+
return join90(getDataDir(), "opencode", "opencode.db");
|
|
96487
96757
|
}
|
|
96488
96758
|
var MAX_MICROTASK_RETRIES = 10;
|
|
96489
96759
|
function tryUpdateMessageModel(db, messageId, targetModel, variant) {
|
|
@@ -96560,7 +96830,7 @@ function retryViaMicrotask(db, messageId, targetModel, variant, attempt) {
|
|
|
96560
96830
|
function scheduleDeferredModelOverride(messageId, targetModel, variant) {
|
|
96561
96831
|
queueMicrotask(() => {
|
|
96562
96832
|
const dbPath = getDbPath();
|
|
96563
|
-
if (!
|
|
96833
|
+
if (!existsSync78(dbPath)) {
|
|
96564
96834
|
log("[ultrawork-db-override] DB not found, skipping deferred override");
|
|
96565
96835
|
return;
|
|
96566
96836
|
}
|
|
@@ -97172,6 +97442,9 @@ function createEventHandler2(args) {
|
|
|
97172
97442
|
if (event.type === "session.status") {
|
|
97173
97443
|
const sessionID = props?.sessionID;
|
|
97174
97444
|
const status = props?.status;
|
|
97445
|
+
if (sessionID && status?.type === "idle") {
|
|
97446
|
+
lastHandledRetryStatusKey.delete(sessionID);
|
|
97447
|
+
}
|
|
97175
97448
|
if (sessionID && status?.type === "retry" && isModelFallbackEnabled && !isRuntimeFallbackEnabled) {
|
|
97176
97449
|
try {
|
|
97177
97450
|
const retryMessage = typeof status.message === "string" ? status.message : "";
|
|
@@ -97294,11 +97567,37 @@ function createToolExecuteAfterHandler3(args) {
|
|
|
97294
97567
|
const prompt = typeof output.metadata?.prompt === "string" ? output.metadata.prompt : undefined;
|
|
97295
97568
|
const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim();
|
|
97296
97569
|
const loopState = directory ? readState(directory) : null;
|
|
97297
|
-
|
|
97570
|
+
const isVerificationContext = agent === "oracle" && !!sessionId && !!directory && loopState?.active === true && loopState.ultrawork === true && loopState.verification_pending === true && loopState.session_id === input.sessionID;
|
|
97571
|
+
log("[tool-execute-after] ULW verification tracking check", {
|
|
97572
|
+
tool: input.tool,
|
|
97573
|
+
agent,
|
|
97574
|
+
parentSessionID: input.sessionID,
|
|
97575
|
+
oracleSessionID: sessionId,
|
|
97576
|
+
hasPromptInMetadata: typeof prompt === "string",
|
|
97577
|
+
extractedVerificationAttemptId: verificationAttemptId
|
|
97578
|
+
});
|
|
97579
|
+
if (isVerificationContext && verificationAttemptId && loopState.verification_attempt_id === verificationAttemptId) {
|
|
97580
|
+
writeState(directory, {
|
|
97581
|
+
...loopState,
|
|
97582
|
+
verification_session_id: sessionId
|
|
97583
|
+
});
|
|
97584
|
+
log("[tool-execute-after] Stored oracle verification session via attempt match", {
|
|
97585
|
+
parentSessionID: input.sessionID,
|
|
97586
|
+
oracleSessionID: sessionId,
|
|
97587
|
+
verificationAttemptId
|
|
97588
|
+
});
|
|
97589
|
+
} else if (isVerificationContext && !verificationAttemptId) {
|
|
97298
97590
|
writeState(directory, {
|
|
97299
97591
|
...loopState,
|
|
97300
97592
|
verification_session_id: sessionId
|
|
97301
97593
|
});
|
|
97594
|
+
log("[tool-execute-after] Fallback: stored oracle verification session without attempt match", {
|
|
97595
|
+
parentSessionID: input.sessionID,
|
|
97596
|
+
oracleSessionID: sessionId,
|
|
97597
|
+
hasPromptInMetadata: typeof prompt === "string",
|
|
97598
|
+
expectedAttemptId: loopState.verification_attempt_id,
|
|
97599
|
+
extractedAttemptId: verificationAttemptId
|
|
97600
|
+
});
|
|
97302
97601
|
}
|
|
97303
97602
|
}
|
|
97304
97603
|
const runToolExecuteAfterHooks = async () => {
|
|
@@ -97416,6 +97715,12 @@ function createToolExecuteBeforeHandler3(args) {
|
|
|
97416
97715
|
const shouldInjectOracleVerification = normalizedSubagentType === "oracle" && loopState?.active === true && loopState.ultrawork === true && loopState.verification_pending === true && loopState.session_id === input.sessionID;
|
|
97417
97716
|
if (shouldInjectOracleVerification) {
|
|
97418
97717
|
const verificationAttemptId = randomUUID4();
|
|
97718
|
+
log("[tool-execute-before] Injecting ULW oracle verification attempt", {
|
|
97719
|
+
sessionID: input.sessionID,
|
|
97720
|
+
callID: input.callID,
|
|
97721
|
+
verificationAttemptId,
|
|
97722
|
+
loopSessionID: loopState.session_id
|
|
97723
|
+
});
|
|
97419
97724
|
writeState(ctx.directory, {
|
|
97420
97725
|
...loopState,
|
|
97421
97726
|
verification_attempt_id: verificationAttemptId,
|