oh-my-opencode 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +702 -0
- package/README.ko.md +3 -3
- package/README.md +3 -3
- package/dist/features/background-agent/manager.d.ts +1 -0
- package/dist/hooks/anthropic-auto-compact/index.d.ts +1 -1
- package/dist/hooks/anthropic-auto-compact/types.d.ts +10 -1
- package/dist/index.js +368 -171
- package/dist/tools/interactive-bash/constants.d.ts +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2257,7 +2257,7 @@ var oracleAgent = {
|
|
|
2257
2257
|
temperature: 0.1,
|
|
2258
2258
|
reasoningEffort: "medium",
|
|
2259
2259
|
textVerbosity: "high",
|
|
2260
|
-
tools: { write: false, edit: false,
|
|
2260
|
+
tools: { write: false, edit: false, task: false, background_task: false },
|
|
2261
2261
|
prompt: `You are a strategic technical advisor with deep reasoning capabilities, operating as a specialized consultant within an AI-assisted development environment.
|
|
2262
2262
|
|
|
2263
2263
|
## Context
|
|
@@ -2331,7 +2331,7 @@ var librarianAgent = {
|
|
|
2331
2331
|
mode: "subagent",
|
|
2332
2332
|
model: "anthropic/claude-sonnet-4-5",
|
|
2333
2333
|
temperature: 0.1,
|
|
2334
|
-
tools: { write: false, edit: false,
|
|
2334
|
+
tools: { write: false, edit: false, background_task: false },
|
|
2335
2335
|
prompt: `# THE LIBRARIAN
|
|
2336
2336
|
|
|
2337
2337
|
You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.
|
|
@@ -2570,7 +2570,7 @@ var exploreAgent = {
|
|
|
2570
2570
|
mode: "subagent",
|
|
2571
2571
|
model: "opencode/grok-code",
|
|
2572
2572
|
temperature: 0.1,
|
|
2573
|
-
tools: { write: false, edit: false,
|
|
2573
|
+
tools: { write: false, edit: false, background_task: false },
|
|
2574
2574
|
prompt: `You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
|
|
2575
2575
|
|
|
2576
2576
|
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
@@ -3119,7 +3119,7 @@ var multimodalLookerAgent = {
|
|
|
3119
3119
|
mode: "subagent",
|
|
3120
3120
|
model: "google/gemini-2.5-flash",
|
|
3121
3121
|
temperature: 0.1,
|
|
3122
|
-
tools: {
|
|
3122
|
+
tools: { write: false, edit: false, bash: false, background_task: false },
|
|
3123
3123
|
prompt: `You interpret media files that cannot be read as plain text.
|
|
3124
3124
|
|
|
3125
3125
|
Your job: examine the attached file and extract ONLY what was requested.
|
|
@@ -3183,7 +3183,11 @@ import { spawn } from "child_process";
|
|
|
3183
3183
|
import { exec } from "child_process";
|
|
3184
3184
|
import { promisify } from "util";
|
|
3185
3185
|
import { existsSync } from "fs";
|
|
3186
|
+
import { homedir } from "os";
|
|
3186
3187
|
var DEFAULT_ZSH_PATHS = ["/bin/zsh", "/usr/bin/zsh", "/usr/local/bin/zsh"];
|
|
3188
|
+
function getHomeDir() {
|
|
3189
|
+
return process.env.HOME || process.env.USERPROFILE || homedir();
|
|
3190
|
+
}
|
|
3187
3191
|
function findZshPath(customZshPath) {
|
|
3188
3192
|
if (customZshPath && existsSync(customZshPath)) {
|
|
3189
3193
|
return customZshPath;
|
|
@@ -3197,7 +3201,7 @@ function findZshPath(customZshPath) {
|
|
|
3197
3201
|
}
|
|
3198
3202
|
var execAsync = promisify(exec);
|
|
3199
3203
|
async function executeHookCommand(command, stdin, cwd, options) {
|
|
3200
|
-
const home =
|
|
3204
|
+
const home = getHomeDir();
|
|
3201
3205
|
let expandedCommand = command.replace(/^~(?=\/|$)/g, home).replace(/\s~(?=\/)/g, ` ${home}`).replace(/\$CLAUDE_PROJECT_DIR/g, cwd).replace(/\$\{CLAUDE_PROJECT_DIR\}/g, cwd);
|
|
3202
3206
|
let finalCommand = expandedCommand;
|
|
3203
3207
|
if (options?.forceZsh) {
|
|
@@ -3692,8 +3696,8 @@ import { join as join4 } from "path";
|
|
|
3692
3696
|
|
|
3693
3697
|
// src/features/hook-message-injector/constants.ts
|
|
3694
3698
|
import { join as join3 } from "path";
|
|
3695
|
-
import { homedir } from "os";
|
|
3696
|
-
var xdgData = process.env.XDG_DATA_HOME || join3(
|
|
3699
|
+
import { homedir as homedir2 } from "os";
|
|
3700
|
+
var xdgData = process.env.XDG_DATA_HOME || join3(homedir2(), ".local", "share");
|
|
3697
3701
|
var OPENCODE_STORAGE = join3(xdgData, "opencode", "storage");
|
|
3698
3702
|
var MESSAGE_STORAGE = join3(OPENCODE_STORAGE, "message");
|
|
3699
3703
|
var PART_STORAGE = join3(OPENCODE_STORAGE, "part");
|
|
@@ -4066,18 +4070,34 @@ function getDefaultSoundPath(p) {
|
|
|
4066
4070
|
}
|
|
4067
4071
|
}
|
|
4068
4072
|
async function sendNotification(ctx, p, title, message) {
|
|
4069
|
-
const escapedTitle = title.replace(/"/g, "\\\"").replace(/'/g, "\\'");
|
|
4070
|
-
const escapedMessage = message.replace(/"/g, "\\\"").replace(/'/g, "\\'");
|
|
4071
4073
|
switch (p) {
|
|
4072
|
-
case "darwin":
|
|
4073
|
-
|
|
4074
|
+
case "darwin": {
|
|
4075
|
+
const esTitle = title.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
4076
|
+
const esMessage = message.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
4077
|
+
await ctx.$`osascript -e ${'display notification "' + esMessage + '" with title "' + esTitle + '"'}`;
|
|
4074
4078
|
break;
|
|
4079
|
+
}
|
|
4075
4080
|
case "linux":
|
|
4076
|
-
await ctx.$`notify-send ${
|
|
4081
|
+
await ctx.$`notify-send ${title} ${message} 2>/dev/null`.catch(() => {});
|
|
4077
4082
|
break;
|
|
4078
|
-
case "win32":
|
|
4079
|
-
|
|
4083
|
+
case "win32": {
|
|
4084
|
+
const psTitle = title.replace(/'/g, "''");
|
|
4085
|
+
const psMessage = message.replace(/'/g, "''");
|
|
4086
|
+
const toastScript = `
|
|
4087
|
+
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
|
|
4088
|
+
$Template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
|
|
4089
|
+
$RawXml = [xml] $Template.GetXml()
|
|
4090
|
+
($RawXml.toast.visual.binding.text | Where-Object {$_.id -eq '1'}).AppendChild($RawXml.CreateTextNode('${psTitle}')) | Out-Null
|
|
4091
|
+
($RawXml.toast.visual.binding.text | Where-Object {$_.id -eq '2'}).AppendChild($RawXml.CreateTextNode('${psMessage}')) | Out-Null
|
|
4092
|
+
$SerializedXml = New-Object Windows.Data.Xml.Dom.XmlDocument
|
|
4093
|
+
$SerializedXml.LoadXml($RawXml.OuterXml)
|
|
4094
|
+
$Toast = [Windows.UI.Notifications.ToastNotification]::new($SerializedXml)
|
|
4095
|
+
$Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('OpenCode')
|
|
4096
|
+
$Notifier.Show($Toast)
|
|
4097
|
+
`.trim().replace(/\n/g, "; ");
|
|
4098
|
+
await ctx.$`powershell -Command ${toastScript}`.catch(() => {});
|
|
4080
4099
|
break;
|
|
4100
|
+
}
|
|
4081
4101
|
}
|
|
4082
4102
|
}
|
|
4083
4103
|
async function playSound(ctx, p, soundPath) {
|
|
@@ -4086,8 +4106,8 @@ async function playSound(ctx, p, soundPath) {
|
|
|
4086
4106
|
ctx.$`afplay ${soundPath}`.catch(() => {});
|
|
4087
4107
|
break;
|
|
4088
4108
|
case "linux":
|
|
4089
|
-
ctx.$`paplay ${soundPath}`.catch(() => {
|
|
4090
|
-
ctx.$`aplay ${soundPath}`.catch(() => {});
|
|
4109
|
+
ctx.$`paplay ${soundPath} 2>/dev/null`.catch(() => {
|
|
4110
|
+
ctx.$`aplay ${soundPath} 2>/dev/null`.catch(() => {});
|
|
4091
4111
|
});
|
|
4092
4112
|
break;
|
|
4093
4113
|
case "win32":
|
|
@@ -4516,7 +4536,25 @@ function getErrorMessage(error) {
|
|
|
4516
4536
|
if (typeof error === "string")
|
|
4517
4537
|
return error.toLowerCase();
|
|
4518
4538
|
const errorObj = error;
|
|
4519
|
-
|
|
4539
|
+
const paths = [
|
|
4540
|
+
errorObj.data,
|
|
4541
|
+
errorObj.error,
|
|
4542
|
+
errorObj,
|
|
4543
|
+
errorObj.data?.error
|
|
4544
|
+
];
|
|
4545
|
+
for (const obj of paths) {
|
|
4546
|
+
if (obj && typeof obj === "object") {
|
|
4547
|
+
const msg = obj.message;
|
|
4548
|
+
if (typeof msg === "string" && msg.length > 0) {
|
|
4549
|
+
return msg.toLowerCase();
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
try {
|
|
4554
|
+
return JSON.stringify(error).toLowerCase();
|
|
4555
|
+
} catch {
|
|
4556
|
+
return "";
|
|
4557
|
+
}
|
|
4520
4558
|
}
|
|
4521
4559
|
function extractMessageIndex(error) {
|
|
4522
4560
|
const message = getErrorMessage(error);
|
|
@@ -4534,7 +4572,7 @@ function detectErrorType(error) {
|
|
|
4534
4572
|
if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
|
|
4535
4573
|
return "thinking_disabled_violation";
|
|
4536
4574
|
}
|
|
4537
|
-
if (message.includes("non-empty content") || message.includes("must have non-empty content")) {
|
|
4575
|
+
if (message.includes("non-empty content") || message.includes("must have non-empty content") || message.includes("content") && message.includes("is empty") || message.includes("content field") && message.includes("empty")) {
|
|
4538
4576
|
return "empty_content_message";
|
|
4539
4577
|
}
|
|
4540
4578
|
return null;
|
|
@@ -4727,15 +4765,16 @@ import { createRequire as createRequire2 } from "module";
|
|
|
4727
4765
|
import { dirname, join as join9 } from "path";
|
|
4728
4766
|
import { existsSync as existsSync7 } from "fs";
|
|
4729
4767
|
import * as fs2 from "fs";
|
|
4768
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
4730
4769
|
|
|
4731
4770
|
// src/hooks/comment-checker/downloader.ts
|
|
4732
4771
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
4733
4772
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
|
|
4734
4773
|
import { join as join8 } from "path";
|
|
4735
|
-
import { homedir as
|
|
4774
|
+
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
4736
4775
|
import { createRequire } from "module";
|
|
4737
4776
|
var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4738
|
-
var DEBUG_FILE = "
|
|
4777
|
+
var DEBUG_FILE = join8(tmpdir2(), "comment-checker-debug.log");
|
|
4739
4778
|
function debugLog(...args) {
|
|
4740
4779
|
if (DEBUG) {
|
|
4741
4780
|
const msg = `[${new Date().toISOString()}] [comment-checker:downloader] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4753,7 +4792,7 @@ var PLATFORM_MAP = {
|
|
|
4753
4792
|
};
|
|
4754
4793
|
function getCacheDir() {
|
|
4755
4794
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
4756
|
-
const base = xdgCache2 || join8(
|
|
4795
|
+
const base = xdgCache2 || join8(homedir3(), ".cache");
|
|
4757
4796
|
return join8(base, "oh-my-opencode", "bin");
|
|
4758
4797
|
}
|
|
4759
4798
|
function getBinaryName() {
|
|
@@ -4863,7 +4902,7 @@ async function ensureCommentCheckerBinary() {
|
|
|
4863
4902
|
|
|
4864
4903
|
// src/hooks/comment-checker/cli.ts
|
|
4865
4904
|
var DEBUG2 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4866
|
-
var DEBUG_FILE2 = "
|
|
4905
|
+
var DEBUG_FILE2 = join9(tmpdir3(), "comment-checker-debug.log");
|
|
4867
4906
|
function debugLog2(...args) {
|
|
4868
4907
|
if (DEBUG2) {
|
|
4869
4908
|
const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4976,8 +5015,10 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4976
5015
|
// src/hooks/comment-checker/index.ts
|
|
4977
5016
|
import * as fs3 from "fs";
|
|
4978
5017
|
import { existsSync as existsSync8 } from "fs";
|
|
5018
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
5019
|
+
import { join as join10 } from "path";
|
|
4979
5020
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4980
|
-
var DEBUG_FILE3 = "
|
|
5021
|
+
var DEBUG_FILE3 = join10(tmpdir4(), "comment-checker-debug.log");
|
|
4981
5022
|
function debugLog3(...args) {
|
|
4982
5023
|
if (DEBUG3) {
|
|
4983
5024
|
const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -5123,7 +5164,7 @@ function createToolOutputTruncatorHook(ctx) {
|
|
|
5123
5164
|
}
|
|
5124
5165
|
// src/hooks/directory-agents-injector/index.ts
|
|
5125
5166
|
import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
|
|
5126
|
-
import { dirname as dirname2, join as
|
|
5167
|
+
import { dirname as dirname2, join as join13, resolve as resolve2 } from "path";
|
|
5127
5168
|
|
|
5128
5169
|
// src/hooks/directory-agents-injector/storage.ts
|
|
5129
5170
|
import {
|
|
@@ -5133,17 +5174,17 @@ import {
|
|
|
5133
5174
|
writeFileSync as writeFileSync3,
|
|
5134
5175
|
unlinkSync as unlinkSync3
|
|
5135
5176
|
} from "fs";
|
|
5136
|
-
import { join as
|
|
5177
|
+
import { join as join12 } from "path";
|
|
5137
5178
|
|
|
5138
5179
|
// src/hooks/directory-agents-injector/constants.ts
|
|
5139
|
-
import { join as
|
|
5140
|
-
var OPENCODE_STORAGE3 =
|
|
5141
|
-
var AGENTS_INJECTOR_STORAGE =
|
|
5180
|
+
import { join as join11 } from "path";
|
|
5181
|
+
var OPENCODE_STORAGE3 = join11(xdgData2 ?? "", "opencode", "storage");
|
|
5182
|
+
var AGENTS_INJECTOR_STORAGE = join11(OPENCODE_STORAGE3, "directory-agents");
|
|
5142
5183
|
var AGENTS_FILENAME = "AGENTS.md";
|
|
5143
5184
|
|
|
5144
5185
|
// src/hooks/directory-agents-injector/storage.ts
|
|
5145
5186
|
function getStoragePath(sessionID) {
|
|
5146
|
-
return
|
|
5187
|
+
return join12(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5147
5188
|
}
|
|
5148
5189
|
function loadInjectedPaths(sessionID) {
|
|
5149
5190
|
const filePath = getStoragePath(sessionID);
|
|
@@ -5195,7 +5236,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
5195
5236
|
const found = [];
|
|
5196
5237
|
let current = startDir;
|
|
5197
5238
|
while (true) {
|
|
5198
|
-
const agentsPath =
|
|
5239
|
+
const agentsPath = join13(current, AGENTS_FILENAME);
|
|
5199
5240
|
if (existsSync10(agentsPath)) {
|
|
5200
5241
|
found.push(agentsPath);
|
|
5201
5242
|
}
|
|
@@ -5264,7 +5305,7 @@ ${content}`;
|
|
|
5264
5305
|
}
|
|
5265
5306
|
// src/hooks/directory-readme-injector/index.ts
|
|
5266
5307
|
import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
|
|
5267
|
-
import { dirname as dirname3, join as
|
|
5308
|
+
import { dirname as dirname3, join as join16, resolve as resolve3 } from "path";
|
|
5268
5309
|
|
|
5269
5310
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5270
5311
|
import {
|
|
@@ -5274,17 +5315,17 @@ import {
|
|
|
5274
5315
|
writeFileSync as writeFileSync4,
|
|
5275
5316
|
unlinkSync as unlinkSync4
|
|
5276
5317
|
} from "fs";
|
|
5277
|
-
import { join as
|
|
5318
|
+
import { join as join15 } from "path";
|
|
5278
5319
|
|
|
5279
5320
|
// src/hooks/directory-readme-injector/constants.ts
|
|
5280
|
-
import { join as
|
|
5281
|
-
var OPENCODE_STORAGE4 =
|
|
5282
|
-
var README_INJECTOR_STORAGE =
|
|
5321
|
+
import { join as join14 } from "path";
|
|
5322
|
+
var OPENCODE_STORAGE4 = join14(xdgData2 ?? "", "opencode", "storage");
|
|
5323
|
+
var README_INJECTOR_STORAGE = join14(OPENCODE_STORAGE4, "directory-readme");
|
|
5283
5324
|
var README_FILENAME = "README.md";
|
|
5284
5325
|
|
|
5285
5326
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5286
5327
|
function getStoragePath2(sessionID) {
|
|
5287
|
-
return
|
|
5328
|
+
return join15(README_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5288
5329
|
}
|
|
5289
5330
|
function loadInjectedPaths2(sessionID) {
|
|
5290
5331
|
const filePath = getStoragePath2(sessionID);
|
|
@@ -5336,7 +5377,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5336
5377
|
const found = [];
|
|
5337
5378
|
let current = startDir;
|
|
5338
5379
|
while (true) {
|
|
5339
|
-
const readmePath =
|
|
5380
|
+
const readmePath = join16(current, README_FILENAME);
|
|
5340
5381
|
if (existsSync12(readmePath)) {
|
|
5341
5382
|
found.push(readmePath);
|
|
5342
5383
|
}
|
|
@@ -5565,11 +5606,15 @@ function parseAnthropicTokenLimitError(err) {
|
|
|
5565
5606
|
|
|
5566
5607
|
// src/hooks/anthropic-auto-compact/types.ts
|
|
5567
5608
|
var RETRY_CONFIG = {
|
|
5568
|
-
maxAttempts:
|
|
5609
|
+
maxAttempts: 2,
|
|
5569
5610
|
initialDelayMs: 2000,
|
|
5570
5611
|
backoffFactor: 2,
|
|
5571
5612
|
maxDelayMs: 30000
|
|
5572
5613
|
};
|
|
5614
|
+
var FALLBACK_CONFIG = {
|
|
5615
|
+
maxRevertAttempts: 3,
|
|
5616
|
+
minMessagesRequired: 2
|
|
5617
|
+
};
|
|
5573
5618
|
|
|
5574
5619
|
// src/hooks/anthropic-auto-compact/executor.ts
|
|
5575
5620
|
function calculateRetryDelay(attempt) {
|
|
@@ -5589,6 +5634,92 @@ function getOrCreateRetryState(autoCompactState, sessionID) {
|
|
|
5589
5634
|
}
|
|
5590
5635
|
return state;
|
|
5591
5636
|
}
|
|
5637
|
+
function getOrCreateFallbackState(autoCompactState, sessionID) {
|
|
5638
|
+
let state = autoCompactState.fallbackStateBySession.get(sessionID);
|
|
5639
|
+
if (!state) {
|
|
5640
|
+
state = { revertAttempt: 0 };
|
|
5641
|
+
autoCompactState.fallbackStateBySession.set(sessionID, state);
|
|
5642
|
+
}
|
|
5643
|
+
return state;
|
|
5644
|
+
}
|
|
5645
|
+
async function getLastMessagePair(sessionID, client, directory) {
|
|
5646
|
+
try {
|
|
5647
|
+
const resp = await client.session.messages({
|
|
5648
|
+
path: { id: sessionID },
|
|
5649
|
+
query: { directory }
|
|
5650
|
+
});
|
|
5651
|
+
const data = resp.data;
|
|
5652
|
+
if (!Array.isArray(data) || data.length < FALLBACK_CONFIG.minMessagesRequired) {
|
|
5653
|
+
return null;
|
|
5654
|
+
}
|
|
5655
|
+
const reversed = [...data].reverse();
|
|
5656
|
+
const lastAssistant = reversed.find((m) => {
|
|
5657
|
+
const msg = m;
|
|
5658
|
+
const info = msg.info;
|
|
5659
|
+
return info?.role === "assistant";
|
|
5660
|
+
});
|
|
5661
|
+
const lastUser = reversed.find((m) => {
|
|
5662
|
+
const msg = m;
|
|
5663
|
+
const info = msg.info;
|
|
5664
|
+
return info?.role === "user";
|
|
5665
|
+
});
|
|
5666
|
+
if (!lastUser)
|
|
5667
|
+
return null;
|
|
5668
|
+
const userInfo = lastUser.info;
|
|
5669
|
+
const userMessageID = userInfo?.id;
|
|
5670
|
+
if (!userMessageID)
|
|
5671
|
+
return null;
|
|
5672
|
+
let assistantMessageID;
|
|
5673
|
+
if (lastAssistant) {
|
|
5674
|
+
const assistantInfo = lastAssistant.info;
|
|
5675
|
+
assistantMessageID = assistantInfo?.id;
|
|
5676
|
+
}
|
|
5677
|
+
return { userMessageID, assistantMessageID };
|
|
5678
|
+
} catch {
|
|
5679
|
+
return null;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
async function executeRevertFallback(sessionID, autoCompactState, client, directory) {
|
|
5683
|
+
const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
|
|
5684
|
+
if (fallbackState.revertAttempt >= FALLBACK_CONFIG.maxRevertAttempts) {
|
|
5685
|
+
return false;
|
|
5686
|
+
}
|
|
5687
|
+
const pair = await getLastMessagePair(sessionID, client, directory);
|
|
5688
|
+
if (!pair) {
|
|
5689
|
+
return false;
|
|
5690
|
+
}
|
|
5691
|
+
await client.tui.showToast({
|
|
5692
|
+
body: {
|
|
5693
|
+
title: "\u26A0\uFE0F Emergency Recovery",
|
|
5694
|
+
message: `Context too large. Removing last message pair to recover session...`,
|
|
5695
|
+
variant: "warning",
|
|
5696
|
+
duration: 4000
|
|
5697
|
+
}
|
|
5698
|
+
}).catch(() => {});
|
|
5699
|
+
try {
|
|
5700
|
+
if (pair.assistantMessageID) {
|
|
5701
|
+
await client.session.revert({
|
|
5702
|
+
path: { id: sessionID },
|
|
5703
|
+
body: { messageID: pair.assistantMessageID },
|
|
5704
|
+
query: { directory }
|
|
5705
|
+
});
|
|
5706
|
+
}
|
|
5707
|
+
await client.session.revert({
|
|
5708
|
+
path: { id: sessionID },
|
|
5709
|
+
body: { messageID: pair.userMessageID },
|
|
5710
|
+
query: { directory }
|
|
5711
|
+
});
|
|
5712
|
+
fallbackState.revertAttempt++;
|
|
5713
|
+
fallbackState.lastRevertedMessageID = pair.userMessageID;
|
|
5714
|
+
const retryState = autoCompactState.retryStateBySession.get(sessionID);
|
|
5715
|
+
if (retryState) {
|
|
5716
|
+
retryState.attempt = 0;
|
|
5717
|
+
}
|
|
5718
|
+
return true;
|
|
5719
|
+
} catch {
|
|
5720
|
+
return false;
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5592
5723
|
async function getLastAssistant(sessionID, client, directory) {
|
|
5593
5724
|
try {
|
|
5594
5725
|
const resp = await client.session.messages({
|
|
@@ -5615,15 +5746,34 @@ function clearSessionState(autoCompactState, sessionID) {
|
|
|
5615
5746
|
autoCompactState.pendingCompact.delete(sessionID);
|
|
5616
5747
|
autoCompactState.errorDataBySession.delete(sessionID);
|
|
5617
5748
|
autoCompactState.retryStateBySession.delete(sessionID);
|
|
5749
|
+
autoCompactState.fallbackStateBySession.delete(sessionID);
|
|
5618
5750
|
}
|
|
5619
5751
|
async function executeCompact(sessionID, msg, autoCompactState, client, directory) {
|
|
5620
5752
|
const retryState = getOrCreateRetryState(autoCompactState, sessionID);
|
|
5621
5753
|
if (!shouldRetry(retryState)) {
|
|
5754
|
+
const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
|
|
5755
|
+
if (fallbackState.revertAttempt < FALLBACK_CONFIG.maxRevertAttempts) {
|
|
5756
|
+
const reverted = await executeRevertFallback(sessionID, autoCompactState, client, directory);
|
|
5757
|
+
if (reverted) {
|
|
5758
|
+
await client.tui.showToast({
|
|
5759
|
+
body: {
|
|
5760
|
+
title: "Recovery Attempt",
|
|
5761
|
+
message: "Message removed. Retrying compaction...",
|
|
5762
|
+
variant: "info",
|
|
5763
|
+
duration: 3000
|
|
5764
|
+
}
|
|
5765
|
+
}).catch(() => {});
|
|
5766
|
+
setTimeout(() => {
|
|
5767
|
+
executeCompact(sessionID, msg, autoCompactState, client, directory);
|
|
5768
|
+
}, 1000);
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5622
5772
|
clearSessionState(autoCompactState, sessionID);
|
|
5623
5773
|
await client.tui.showToast({
|
|
5624
5774
|
body: {
|
|
5625
5775
|
title: "Auto Compact Failed",
|
|
5626
|
-
message: `Failed after ${RETRY_CONFIG.maxAttempts}
|
|
5776
|
+
message: `Failed after ${RETRY_CONFIG.maxAttempts} retries and ${FALLBACK_CONFIG.maxRevertAttempts} message removals. Please start a new session.`,
|
|
5627
5777
|
variant: "error",
|
|
5628
5778
|
duration: 5000
|
|
5629
5779
|
}
|
|
@@ -5669,7 +5819,8 @@ function createAutoCompactState() {
|
|
|
5669
5819
|
return {
|
|
5670
5820
|
pendingCompact: new Set,
|
|
5671
5821
|
errorDataBySession: new Map,
|
|
5672
|
-
retryStateBySession: new Map
|
|
5822
|
+
retryStateBySession: new Map,
|
|
5823
|
+
fallbackStateBySession: new Map
|
|
5673
5824
|
};
|
|
5674
5825
|
}
|
|
5675
5826
|
function createAnthropicAutoCompactHook(ctx) {
|
|
@@ -5682,6 +5833,7 @@ function createAnthropicAutoCompactHook(ctx) {
|
|
|
5682
5833
|
autoCompactState.pendingCompact.delete(sessionInfo.id);
|
|
5683
5834
|
autoCompactState.errorDataBySession.delete(sessionInfo.id);
|
|
5684
5835
|
autoCompactState.retryStateBySession.delete(sessionInfo.id);
|
|
5836
|
+
autoCompactState.fallbackStateBySession.delete(sessionInfo.id);
|
|
5685
5837
|
}
|
|
5686
5838
|
return;
|
|
5687
5839
|
}
|
|
@@ -6024,8 +6176,8 @@ function createThinkModeHook() {
|
|
|
6024
6176
|
};
|
|
6025
6177
|
}
|
|
6026
6178
|
// src/hooks/claude-code-hooks/config.ts
|
|
6027
|
-
import { homedir as
|
|
6028
|
-
import { join as
|
|
6179
|
+
import { homedir as homedir4 } from "os";
|
|
6180
|
+
import { join as join17 } from "path";
|
|
6029
6181
|
import { existsSync as existsSync13 } from "fs";
|
|
6030
6182
|
function normalizeHookMatcher(raw) {
|
|
6031
6183
|
return {
|
|
@@ -6049,11 +6201,11 @@ function normalizeHooksConfig(raw) {
|
|
|
6049
6201
|
return result;
|
|
6050
6202
|
}
|
|
6051
6203
|
function getClaudeSettingsPaths(customPath) {
|
|
6052
|
-
const home =
|
|
6204
|
+
const home = homedir4();
|
|
6053
6205
|
const paths = [
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6206
|
+
join17(home, ".claude", "settings.json"),
|
|
6207
|
+
join17(process.cwd(), ".claude", "settings.json"),
|
|
6208
|
+
join17(process.cwd(), ".claude", "settings.local.json")
|
|
6057
6209
|
];
|
|
6058
6210
|
if (customPath && existsSync13(customPath)) {
|
|
6059
6211
|
paths.unshift(customPath);
|
|
@@ -6097,11 +6249,11 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6097
6249
|
|
|
6098
6250
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6099
6251
|
import { existsSync as existsSync14 } from "fs";
|
|
6100
|
-
import { homedir as
|
|
6101
|
-
import { join as
|
|
6102
|
-
var USER_CONFIG_PATH =
|
|
6252
|
+
import { homedir as homedir5 } from "os";
|
|
6253
|
+
import { join as join18 } from "path";
|
|
6254
|
+
var USER_CONFIG_PATH = join18(homedir5(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
6103
6255
|
function getProjectConfigPath() {
|
|
6104
|
-
return
|
|
6256
|
+
return join18(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6105
6257
|
}
|
|
6106
6258
|
async function loadConfigFromPath(path3) {
|
|
6107
6259
|
if (!existsSync14(path3)) {
|
|
@@ -6169,8 +6321,9 @@ function isHookCommandDisabled(eventType, command, config) {
|
|
|
6169
6321
|
}
|
|
6170
6322
|
|
|
6171
6323
|
// src/hooks/claude-code-hooks/plugin-config.ts
|
|
6324
|
+
var isWindows = process.platform === "win32";
|
|
6172
6325
|
var DEFAULT_CONFIG = {
|
|
6173
|
-
forceZsh:
|
|
6326
|
+
forceZsh: !isWindows,
|
|
6174
6327
|
zshPath: "/bin/zsh"
|
|
6175
6328
|
};
|
|
6176
6329
|
|
|
@@ -6282,13 +6435,13 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
6282
6435
|
}
|
|
6283
6436
|
|
|
6284
6437
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
6285
|
-
import { join as
|
|
6438
|
+
import { join as join19 } from "path";
|
|
6286
6439
|
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync15, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
6287
|
-
import { homedir as
|
|
6440
|
+
import { homedir as homedir6, tmpdir as tmpdir5 } from "os";
|
|
6288
6441
|
import { randomUUID } from "crypto";
|
|
6289
|
-
var TRANSCRIPT_DIR =
|
|
6442
|
+
var TRANSCRIPT_DIR = join19(homedir6(), ".claude", "transcripts");
|
|
6290
6443
|
function getTranscriptPath(sessionId) {
|
|
6291
|
-
return
|
|
6444
|
+
return join19(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
6292
6445
|
}
|
|
6293
6446
|
function ensureTranscriptDir() {
|
|
6294
6447
|
if (!existsSync15(TRANSCRIPT_DIR)) {
|
|
@@ -6378,7 +6531,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6378
6531
|
}
|
|
6379
6532
|
};
|
|
6380
6533
|
entries.push(JSON.stringify(currentEntry));
|
|
6381
|
-
const tempPath =
|
|
6534
|
+
const tempPath = join19(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6382
6535
|
writeFileSync5(tempPath, entries.join(`
|
|
6383
6536
|
`) + `
|
|
6384
6537
|
`);
|
|
@@ -6398,7 +6551,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6398
6551
|
]
|
|
6399
6552
|
}
|
|
6400
6553
|
};
|
|
6401
|
-
const tempPath =
|
|
6554
|
+
const tempPath = join19(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6402
6555
|
writeFileSync5(tempPath, JSON.stringify(currentEntry) + `
|
|
6403
6556
|
`);
|
|
6404
6557
|
return tempPath;
|
|
@@ -6610,11 +6763,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
6610
6763
|
}
|
|
6611
6764
|
|
|
6612
6765
|
// src/hooks/claude-code-hooks/todo.ts
|
|
6613
|
-
import { join as
|
|
6614
|
-
import { homedir as
|
|
6615
|
-
var TODO_DIR =
|
|
6766
|
+
import { join as join20 } from "path";
|
|
6767
|
+
import { homedir as homedir7 } from "os";
|
|
6768
|
+
var TODO_DIR = join20(homedir7(), ".claude", "todos");
|
|
6616
6769
|
function getTodoPath(sessionId) {
|
|
6617
|
-
return
|
|
6770
|
+
return join20(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
6618
6771
|
}
|
|
6619
6772
|
|
|
6620
6773
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -6946,7 +7099,7 @@ ${result.message}`;
|
|
|
6946
7099
|
}
|
|
6947
7100
|
// src/hooks/rules-injector/index.ts
|
|
6948
7101
|
import { readFileSync as readFileSync9 } from "fs";
|
|
6949
|
-
import { homedir as
|
|
7102
|
+
import { homedir as homedir8 } from "os";
|
|
6950
7103
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
6951
7104
|
|
|
6952
7105
|
// src/hooks/rules-injector/finder.ts
|
|
@@ -6956,12 +7109,12 @@ import {
|
|
|
6956
7109
|
realpathSync,
|
|
6957
7110
|
statSync as statSync2
|
|
6958
7111
|
} from "fs";
|
|
6959
|
-
import { dirname as dirname4, join as
|
|
7112
|
+
import { dirname as dirname4, join as join22, relative } from "path";
|
|
6960
7113
|
|
|
6961
7114
|
// src/hooks/rules-injector/constants.ts
|
|
6962
|
-
import { join as
|
|
6963
|
-
var OPENCODE_STORAGE5 =
|
|
6964
|
-
var RULES_INJECTOR_STORAGE =
|
|
7115
|
+
import { join as join21 } from "path";
|
|
7116
|
+
var OPENCODE_STORAGE5 = join21(xdgData2 ?? "", "opencode", "storage");
|
|
7117
|
+
var RULES_INJECTOR_STORAGE = join21(OPENCODE_STORAGE5, "rules-injector");
|
|
6965
7118
|
var PROJECT_MARKERS = [
|
|
6966
7119
|
".git",
|
|
6967
7120
|
"pyproject.toml",
|
|
@@ -6988,7 +7141,7 @@ function findProjectRoot(startPath) {
|
|
|
6988
7141
|
}
|
|
6989
7142
|
while (true) {
|
|
6990
7143
|
for (const marker of PROJECT_MARKERS) {
|
|
6991
|
-
const markerPath =
|
|
7144
|
+
const markerPath = join22(current, marker);
|
|
6992
7145
|
if (existsSync16(markerPath)) {
|
|
6993
7146
|
return current;
|
|
6994
7147
|
}
|
|
@@ -7006,7 +7159,7 @@ function findRuleFilesRecursive(dir, results) {
|
|
|
7006
7159
|
try {
|
|
7007
7160
|
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
7008
7161
|
for (const entry of entries) {
|
|
7009
|
-
const fullPath =
|
|
7162
|
+
const fullPath = join22(dir, entry.name);
|
|
7010
7163
|
if (entry.isDirectory()) {
|
|
7011
7164
|
findRuleFilesRecursive(fullPath, results);
|
|
7012
7165
|
} else if (entry.isFile()) {
|
|
@@ -7032,7 +7185,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7032
7185
|
let distance = 0;
|
|
7033
7186
|
while (true) {
|
|
7034
7187
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
7035
|
-
const ruleDir =
|
|
7188
|
+
const ruleDir = join22(currentDir, parent, subdir);
|
|
7036
7189
|
const files = [];
|
|
7037
7190
|
findRuleFilesRecursive(ruleDir, files);
|
|
7038
7191
|
for (const filePath of files) {
|
|
@@ -7056,7 +7209,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7056
7209
|
currentDir = parentDir;
|
|
7057
7210
|
distance++;
|
|
7058
7211
|
}
|
|
7059
|
-
const userRuleDir =
|
|
7212
|
+
const userRuleDir = join22(homeDir, USER_RULE_DIR);
|
|
7060
7213
|
const userFiles = [];
|
|
7061
7214
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
7062
7215
|
for (const filePath of userFiles) {
|
|
@@ -7251,9 +7404,9 @@ import {
|
|
|
7251
7404
|
writeFileSync as writeFileSync6,
|
|
7252
7405
|
unlinkSync as unlinkSync6
|
|
7253
7406
|
} from "fs";
|
|
7254
|
-
import { join as
|
|
7407
|
+
import { join as join23 } from "path";
|
|
7255
7408
|
function getStoragePath3(sessionID) {
|
|
7256
|
-
return
|
|
7409
|
+
return join23(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
7257
7410
|
}
|
|
7258
7411
|
function loadInjectedRules(sessionID) {
|
|
7259
7412
|
const filePath = getStoragePath3(sessionID);
|
|
@@ -7314,7 +7467,7 @@ function createRulesInjectorHook(ctx) {
|
|
|
7314
7467
|
return;
|
|
7315
7468
|
const projectRoot = findProjectRoot(filePath);
|
|
7316
7469
|
const cache2 = getSessionCache(input.sessionID);
|
|
7317
|
-
const home =
|
|
7470
|
+
const home = homedir8();
|
|
7318
7471
|
const ruleFileCandidates = findRuleFiles(projectRoot, home, filePath);
|
|
7319
7472
|
const toInject = [];
|
|
7320
7473
|
for (const candidate of ruleFileCandidates) {
|
|
@@ -7692,12 +7845,12 @@ import {
|
|
|
7692
7845
|
writeFileSync as writeFileSync8,
|
|
7693
7846
|
unlinkSync as unlinkSync7
|
|
7694
7847
|
} from "fs";
|
|
7695
|
-
import { join as
|
|
7848
|
+
import { join as join28 } from "path";
|
|
7696
7849
|
|
|
7697
7850
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
7698
|
-
import { join as
|
|
7699
|
-
var OPENCODE_STORAGE6 =
|
|
7700
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
7851
|
+
import { join as join27 } from "path";
|
|
7852
|
+
var OPENCODE_STORAGE6 = join27(xdgData2 ?? "", "opencode", "storage");
|
|
7853
|
+
var AGENT_USAGE_REMINDER_STORAGE = join27(OPENCODE_STORAGE6, "agent-usage-reminder");
|
|
7701
7854
|
var TARGET_TOOLS = new Set([
|
|
7702
7855
|
"grep",
|
|
7703
7856
|
"safe_grep",
|
|
@@ -7742,7 +7895,7 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
|
|
7742
7895
|
|
|
7743
7896
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
7744
7897
|
function getStoragePath4(sessionID) {
|
|
7745
|
-
return
|
|
7898
|
+
return join28(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
7746
7899
|
}
|
|
7747
7900
|
function loadAgentUsageState(sessionID) {
|
|
7748
7901
|
const filePath = getStoragePath4(sessionID);
|
|
@@ -7991,12 +8144,12 @@ import {
|
|
|
7991
8144
|
writeFileSync as writeFileSync9,
|
|
7992
8145
|
unlinkSync as unlinkSync8
|
|
7993
8146
|
} from "fs";
|
|
7994
|
-
import { join as
|
|
8147
|
+
import { join as join30 } from "path";
|
|
7995
8148
|
|
|
7996
8149
|
// src/hooks/interactive-bash-session/constants.ts
|
|
7997
|
-
import { join as
|
|
7998
|
-
var OPENCODE_STORAGE7 =
|
|
7999
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
8150
|
+
import { join as join29 } from "path";
|
|
8151
|
+
var OPENCODE_STORAGE7 = join29(xdgData2 ?? "", "opencode", "storage");
|
|
8152
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join29(OPENCODE_STORAGE7, "interactive-bash-session");
|
|
8000
8153
|
var OMO_SESSION_PREFIX = "omo-";
|
|
8001
8154
|
function buildSessionReminderMessage(sessions) {
|
|
8002
8155
|
if (sessions.length === 0)
|
|
@@ -8008,7 +8161,7 @@ function buildSessionReminderMessage(sessions) {
|
|
|
8008
8161
|
|
|
8009
8162
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8010
8163
|
function getStoragePath5(sessionID) {
|
|
8011
|
-
return
|
|
8164
|
+
return join30(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
8012
8165
|
}
|
|
8013
8166
|
function loadInteractiveBashSessionState(sessionID) {
|
|
8014
8167
|
const filePath = getStoragePath5(sessionID);
|
|
@@ -9782,8 +9935,8 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
9782
9935
|
}
|
|
9783
9936
|
// src/features/claude-code-command-loader/loader.ts
|
|
9784
9937
|
import { existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "fs";
|
|
9785
|
-
import { homedir as
|
|
9786
|
-
import { join as
|
|
9938
|
+
import { homedir as homedir10 } from "os";
|
|
9939
|
+
import { join as join31, basename } from "path";
|
|
9787
9940
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
9788
9941
|
if (!existsSync22(commandsDir)) {
|
|
9789
9942
|
return [];
|
|
@@ -9793,7 +9946,7 @@ function loadCommandsFromDir(commandsDir, scope) {
|
|
|
9793
9946
|
for (const entry of entries) {
|
|
9794
9947
|
if (!isMarkdownFile(entry))
|
|
9795
9948
|
continue;
|
|
9796
|
-
const commandPath =
|
|
9949
|
+
const commandPath = join31(commandsDir, entry.name);
|
|
9797
9950
|
const commandName = basename(entry.name, ".md");
|
|
9798
9951
|
try {
|
|
9799
9952
|
const content = readFileSync14(commandPath, "utf-8");
|
|
@@ -9836,29 +9989,29 @@ function commandsToRecord(commands) {
|
|
|
9836
9989
|
return result;
|
|
9837
9990
|
}
|
|
9838
9991
|
function loadUserCommands() {
|
|
9839
|
-
const userCommandsDir =
|
|
9992
|
+
const userCommandsDir = join31(homedir10(), ".claude", "commands");
|
|
9840
9993
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
9841
9994
|
return commandsToRecord(commands);
|
|
9842
9995
|
}
|
|
9843
9996
|
function loadProjectCommands() {
|
|
9844
|
-
const projectCommandsDir =
|
|
9997
|
+
const projectCommandsDir = join31(process.cwd(), ".claude", "commands");
|
|
9845
9998
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
9846
9999
|
return commandsToRecord(commands);
|
|
9847
10000
|
}
|
|
9848
10001
|
function loadOpencodeGlobalCommands() {
|
|
9849
|
-
const opencodeCommandsDir =
|
|
10002
|
+
const opencodeCommandsDir = join31(homedir10(), ".config", "opencode", "command");
|
|
9850
10003
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
9851
10004
|
return commandsToRecord(commands);
|
|
9852
10005
|
}
|
|
9853
10006
|
function loadOpencodeProjectCommands() {
|
|
9854
|
-
const opencodeProjectDir =
|
|
10007
|
+
const opencodeProjectDir = join31(process.cwd(), ".opencode", "command");
|
|
9855
10008
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
9856
10009
|
return commandsToRecord(commands);
|
|
9857
10010
|
}
|
|
9858
10011
|
// src/features/claude-code-skill-loader/loader.ts
|
|
9859
10012
|
import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
|
|
9860
|
-
import { homedir as
|
|
9861
|
-
import { join as
|
|
10013
|
+
import { homedir as homedir11 } from "os";
|
|
10014
|
+
import { join as join32 } from "path";
|
|
9862
10015
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
9863
10016
|
if (!existsSync23(skillsDir)) {
|
|
9864
10017
|
return [];
|
|
@@ -9868,11 +10021,11 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
9868
10021
|
for (const entry of entries) {
|
|
9869
10022
|
if (entry.name.startsWith("."))
|
|
9870
10023
|
continue;
|
|
9871
|
-
const skillPath =
|
|
10024
|
+
const skillPath = join32(skillsDir, entry.name);
|
|
9872
10025
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
9873
10026
|
continue;
|
|
9874
10027
|
const resolvedPath = resolveSymlink(skillPath);
|
|
9875
|
-
const skillMdPath =
|
|
10028
|
+
const skillMdPath = join32(resolvedPath, "SKILL.md");
|
|
9876
10029
|
if (!existsSync23(skillMdPath))
|
|
9877
10030
|
continue;
|
|
9878
10031
|
try {
|
|
@@ -9907,7 +10060,7 @@ $ARGUMENTS
|
|
|
9907
10060
|
return skills;
|
|
9908
10061
|
}
|
|
9909
10062
|
function loadUserSkillsAsCommands() {
|
|
9910
|
-
const userSkillsDir =
|
|
10063
|
+
const userSkillsDir = join32(homedir11(), ".claude", "skills");
|
|
9911
10064
|
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
9912
10065
|
return skills.reduce((acc, skill) => {
|
|
9913
10066
|
acc[skill.name] = skill.definition;
|
|
@@ -9915,7 +10068,7 @@ function loadUserSkillsAsCommands() {
|
|
|
9915
10068
|
}, {});
|
|
9916
10069
|
}
|
|
9917
10070
|
function loadProjectSkillsAsCommands() {
|
|
9918
|
-
const projectSkillsDir =
|
|
10071
|
+
const projectSkillsDir = join32(process.cwd(), ".claude", "skills");
|
|
9919
10072
|
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
9920
10073
|
return skills.reduce((acc, skill) => {
|
|
9921
10074
|
acc[skill.name] = skill.definition;
|
|
@@ -9924,8 +10077,8 @@ function loadProjectSkillsAsCommands() {
|
|
|
9924
10077
|
}
|
|
9925
10078
|
// src/features/claude-code-agent-loader/loader.ts
|
|
9926
10079
|
import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
|
|
9927
|
-
import { homedir as
|
|
9928
|
-
import { join as
|
|
10080
|
+
import { homedir as homedir12 } from "os";
|
|
10081
|
+
import { join as join33, basename as basename2 } from "path";
|
|
9929
10082
|
function parseToolsConfig(toolsStr) {
|
|
9930
10083
|
if (!toolsStr)
|
|
9931
10084
|
return;
|
|
@@ -9947,7 +10100,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
9947
10100
|
for (const entry of entries) {
|
|
9948
10101
|
if (!isMarkdownFile(entry))
|
|
9949
10102
|
continue;
|
|
9950
|
-
const agentPath =
|
|
10103
|
+
const agentPath = join33(agentsDir, entry.name);
|
|
9951
10104
|
const agentName = basename2(entry.name, ".md");
|
|
9952
10105
|
try {
|
|
9953
10106
|
const content = readFileSync16(agentPath, "utf-8");
|
|
@@ -9977,7 +10130,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
9977
10130
|
return agents;
|
|
9978
10131
|
}
|
|
9979
10132
|
function loadUserAgents() {
|
|
9980
|
-
const userAgentsDir =
|
|
10133
|
+
const userAgentsDir = join33(homedir12(), ".claude", "agents");
|
|
9981
10134
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
9982
10135
|
const result = {};
|
|
9983
10136
|
for (const agent of agents) {
|
|
@@ -9986,7 +10139,7 @@ function loadUserAgents() {
|
|
|
9986
10139
|
return result;
|
|
9987
10140
|
}
|
|
9988
10141
|
function loadProjectAgents() {
|
|
9989
|
-
const projectAgentsDir =
|
|
10142
|
+
const projectAgentsDir = join33(process.cwd(), ".claude", "agents");
|
|
9990
10143
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
9991
10144
|
const result = {};
|
|
9992
10145
|
for (const agent of agents) {
|
|
@@ -9996,8 +10149,8 @@ function loadProjectAgents() {
|
|
|
9996
10149
|
}
|
|
9997
10150
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
9998
10151
|
import { existsSync as existsSync25 } from "fs";
|
|
9999
|
-
import { homedir as
|
|
10000
|
-
import { join as
|
|
10152
|
+
import { homedir as homedir13 } from "os";
|
|
10153
|
+
import { join as join34 } from "path";
|
|
10001
10154
|
|
|
10002
10155
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
10003
10156
|
function expandEnvVars(value) {
|
|
@@ -10063,12 +10216,12 @@ function transformMcpServer(name, server) {
|
|
|
10063
10216
|
|
|
10064
10217
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
10065
10218
|
function getMcpConfigPaths() {
|
|
10066
|
-
const home =
|
|
10219
|
+
const home = homedir13();
|
|
10067
10220
|
const cwd = process.cwd();
|
|
10068
10221
|
return [
|
|
10069
|
-
{ path:
|
|
10070
|
-
{ path:
|
|
10071
|
-
{ path:
|
|
10222
|
+
{ path: join34(home, ".claude", ".mcp.json"), scope: "user" },
|
|
10223
|
+
{ path: join34(cwd, ".mcp.json"), scope: "project" },
|
|
10224
|
+
{ path: join34(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
10072
10225
|
];
|
|
10073
10226
|
}
|
|
10074
10227
|
async function loadMcpConfigFile(filePath) {
|
|
@@ -10359,8 +10512,8 @@ var EXT_TO_LANG = {
|
|
|
10359
10512
|
};
|
|
10360
10513
|
// src/tools/lsp/config.ts
|
|
10361
10514
|
import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
|
|
10362
|
-
import { join as
|
|
10363
|
-
import { homedir as
|
|
10515
|
+
import { join as join35 } from "path";
|
|
10516
|
+
import { homedir as homedir14 } from "os";
|
|
10364
10517
|
function loadJsonFile(path6) {
|
|
10365
10518
|
if (!existsSync26(path6))
|
|
10366
10519
|
return null;
|
|
@@ -10373,9 +10526,9 @@ function loadJsonFile(path6) {
|
|
|
10373
10526
|
function getConfigPaths2() {
|
|
10374
10527
|
const cwd = process.cwd();
|
|
10375
10528
|
return {
|
|
10376
|
-
project:
|
|
10377
|
-
user:
|
|
10378
|
-
opencode:
|
|
10529
|
+
project: join35(cwd, ".opencode", "oh-my-opencode.json"),
|
|
10530
|
+
user: join35(homedir14(), ".config", "opencode", "oh-my-opencode.json"),
|
|
10531
|
+
opencode: join35(homedir14(), ".config", "opencode", "opencode.json")
|
|
10379
10532
|
};
|
|
10380
10533
|
}
|
|
10381
10534
|
function loadAllConfigs() {
|
|
@@ -10468,7 +10621,7 @@ function isServerInstalled(command) {
|
|
|
10468
10621
|
const pathEnv = process.env.PATH || "";
|
|
10469
10622
|
const paths = pathEnv.split(":");
|
|
10470
10623
|
for (const p of paths) {
|
|
10471
|
-
if (existsSync26(
|
|
10624
|
+
if (existsSync26(join35(p, cmd))) {
|
|
10472
10625
|
return true;
|
|
10473
10626
|
}
|
|
10474
10627
|
}
|
|
@@ -23967,14 +24120,14 @@ var lsp_code_action_resolve = tool({
|
|
|
23967
24120
|
});
|
|
23968
24121
|
// src/tools/ast-grep/constants.ts
|
|
23969
24122
|
import { createRequire as createRequire4 } from "module";
|
|
23970
|
-
import { dirname as dirname6, join as
|
|
24123
|
+
import { dirname as dirname6, join as join37 } from "path";
|
|
23971
24124
|
import { existsSync as existsSync29, statSync as statSync4 } from "fs";
|
|
23972
24125
|
|
|
23973
24126
|
// src/tools/ast-grep/downloader.ts
|
|
23974
24127
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
23975
24128
|
import { existsSync as existsSync28, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
23976
|
-
import { join as
|
|
23977
|
-
import { homedir as
|
|
24129
|
+
import { join as join36 } from "path";
|
|
24130
|
+
import { homedir as homedir15 } from "os";
|
|
23978
24131
|
import { createRequire as createRequire3 } from "module";
|
|
23979
24132
|
var REPO2 = "ast-grep/ast-grep";
|
|
23980
24133
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -23999,18 +24152,18 @@ var PLATFORM_MAP2 = {
|
|
|
23999
24152
|
function getCacheDir3() {
|
|
24000
24153
|
if (process.platform === "win32") {
|
|
24001
24154
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
24002
|
-
const base2 = localAppData ||
|
|
24003
|
-
return
|
|
24155
|
+
const base2 = localAppData || join36(homedir15(), "AppData", "Local");
|
|
24156
|
+
return join36(base2, "oh-my-opencode", "bin");
|
|
24004
24157
|
}
|
|
24005
24158
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
24006
|
-
const base = xdgCache2 ||
|
|
24007
|
-
return
|
|
24159
|
+
const base = xdgCache2 || join36(homedir15(), ".cache");
|
|
24160
|
+
return join36(base, "oh-my-opencode", "bin");
|
|
24008
24161
|
}
|
|
24009
24162
|
function getBinaryName3() {
|
|
24010
24163
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
24011
24164
|
}
|
|
24012
24165
|
function getCachedBinaryPath2() {
|
|
24013
|
-
const binaryPath =
|
|
24166
|
+
const binaryPath = join36(getCacheDir3(), getBinaryName3());
|
|
24014
24167
|
return existsSync28(binaryPath) ? binaryPath : null;
|
|
24015
24168
|
}
|
|
24016
24169
|
async function extractZip2(archivePath, destDir) {
|
|
@@ -24037,7 +24190,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24037
24190
|
}
|
|
24038
24191
|
const cacheDir = getCacheDir3();
|
|
24039
24192
|
const binaryName = getBinaryName3();
|
|
24040
|
-
const binaryPath =
|
|
24193
|
+
const binaryPath = join36(cacheDir, binaryName);
|
|
24041
24194
|
if (existsSync28(binaryPath)) {
|
|
24042
24195
|
return binaryPath;
|
|
24043
24196
|
}
|
|
@@ -24053,7 +24206,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
24053
24206
|
if (!response2.ok) {
|
|
24054
24207
|
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
24055
24208
|
}
|
|
24056
|
-
const archivePath =
|
|
24209
|
+
const archivePath = join36(cacheDir, assetName);
|
|
24057
24210
|
const arrayBuffer = await response2.arrayBuffer();
|
|
24058
24211
|
await Bun.write(archivePath, arrayBuffer);
|
|
24059
24212
|
await extractZip2(archivePath, cacheDir);
|
|
@@ -24111,7 +24264,7 @@ function findSgCliPathSync() {
|
|
|
24111
24264
|
const require2 = createRequire4(import.meta.url);
|
|
24112
24265
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
24113
24266
|
const cliDir = dirname6(cliPkgPath);
|
|
24114
|
-
const sgPath =
|
|
24267
|
+
const sgPath = join37(cliDir, binaryName);
|
|
24115
24268
|
if (existsSync29(sgPath) && isValidBinary(sgPath)) {
|
|
24116
24269
|
return sgPath;
|
|
24117
24270
|
}
|
|
@@ -24123,7 +24276,7 @@ function findSgCliPathSync() {
|
|
|
24123
24276
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
24124
24277
|
const pkgDir = dirname6(pkgPath);
|
|
24125
24278
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
24126
|
-
const binaryPath =
|
|
24279
|
+
const binaryPath = join37(pkgDir, astGrepName);
|
|
24127
24280
|
if (existsSync29(binaryPath) && isValidBinary(binaryPath)) {
|
|
24128
24281
|
return binaryPath;
|
|
24129
24282
|
}
|
|
@@ -24499,19 +24652,19 @@ var {spawn: spawn7 } = globalThis.Bun;
|
|
|
24499
24652
|
|
|
24500
24653
|
// src/tools/grep/constants.ts
|
|
24501
24654
|
import { existsSync as existsSync32 } from "fs";
|
|
24502
|
-
import { join as
|
|
24655
|
+
import { join as join39, dirname as dirname7 } from "path";
|
|
24503
24656
|
import { spawnSync } from "child_process";
|
|
24504
24657
|
|
|
24505
24658
|
// src/tools/grep/downloader.ts
|
|
24506
24659
|
import { existsSync as existsSync31, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync8 } from "fs";
|
|
24507
|
-
import { join as
|
|
24660
|
+
import { join as join38 } from "path";
|
|
24508
24661
|
function getInstallDir() {
|
|
24509
24662
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
24510
|
-
return
|
|
24663
|
+
return join38(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
24511
24664
|
}
|
|
24512
24665
|
function getRgPath() {
|
|
24513
|
-
const
|
|
24514
|
-
return
|
|
24666
|
+
const isWindows2 = process.platform === "win32";
|
|
24667
|
+
return join38(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
24515
24668
|
}
|
|
24516
24669
|
function getInstalledRipgrepPath() {
|
|
24517
24670
|
const rgPath = getRgPath();
|
|
@@ -24521,8 +24674,8 @@ function getInstalledRipgrepPath() {
|
|
|
24521
24674
|
// src/tools/grep/constants.ts
|
|
24522
24675
|
var cachedCli = null;
|
|
24523
24676
|
function findExecutable(name) {
|
|
24524
|
-
const
|
|
24525
|
-
const cmd =
|
|
24677
|
+
const isWindows2 = process.platform === "win32";
|
|
24678
|
+
const cmd = isWindows2 ? "where" : "which";
|
|
24526
24679
|
try {
|
|
24527
24680
|
const result = spawnSync(cmd, [name], { encoding: "utf-8", timeout: 5000 });
|
|
24528
24681
|
if (result.status === 0 && result.stdout.trim()) {
|
|
@@ -24535,13 +24688,13 @@ function findExecutable(name) {
|
|
|
24535
24688
|
function getOpenCodeBundledRg() {
|
|
24536
24689
|
const execPath = process.execPath;
|
|
24537
24690
|
const execDir = dirname7(execPath);
|
|
24538
|
-
const
|
|
24539
|
-
const rgName =
|
|
24691
|
+
const isWindows2 = process.platform === "win32";
|
|
24692
|
+
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
24540
24693
|
const candidates = [
|
|
24541
|
-
|
|
24542
|
-
|
|
24543
|
-
|
|
24544
|
-
|
|
24694
|
+
join39(execDir, rgName),
|
|
24695
|
+
join39(execDir, "bin", rgName),
|
|
24696
|
+
join39(execDir, "..", "bin", rgName),
|
|
24697
|
+
join39(execDir, "..", "libexec", rgName)
|
|
24545
24698
|
];
|
|
24546
24699
|
for (const candidate of candidates) {
|
|
24547
24700
|
if (existsSync32(candidate)) {
|
|
@@ -24948,8 +25101,8 @@ var glob = tool({
|
|
|
24948
25101
|
});
|
|
24949
25102
|
// src/tools/slashcommand/tools.ts
|
|
24950
25103
|
import { existsSync as existsSync33, readdirSync as readdirSync9, readFileSync as readFileSync20 } from "fs";
|
|
24951
|
-
import { homedir as
|
|
24952
|
-
import { join as
|
|
25104
|
+
import { homedir as homedir16 } from "os";
|
|
25105
|
+
import { join as join40, basename as basename3, dirname as dirname8 } from "path";
|
|
24953
25106
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
24954
25107
|
if (!existsSync33(commandsDir)) {
|
|
24955
25108
|
return [];
|
|
@@ -24959,7 +25112,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
24959
25112
|
for (const entry of entries) {
|
|
24960
25113
|
if (!isMarkdownFile(entry))
|
|
24961
25114
|
continue;
|
|
24962
|
-
const commandPath =
|
|
25115
|
+
const commandPath = join40(commandsDir, entry.name);
|
|
24963
25116
|
const commandName = basename3(entry.name, ".md");
|
|
24964
25117
|
try {
|
|
24965
25118
|
const content = readFileSync20(commandPath, "utf-8");
|
|
@@ -24987,10 +25140,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
24987
25140
|
return commands;
|
|
24988
25141
|
}
|
|
24989
25142
|
function discoverCommandsSync() {
|
|
24990
|
-
const userCommandsDir =
|
|
24991
|
-
const projectCommandsDir =
|
|
24992
|
-
const opencodeGlobalDir =
|
|
24993
|
-
const opencodeProjectDir =
|
|
25143
|
+
const userCommandsDir = join40(homedir16(), ".claude", "commands");
|
|
25144
|
+
const projectCommandsDir = join40(process.cwd(), ".claude", "commands");
|
|
25145
|
+
const opencodeGlobalDir = join40(homedir16(), ".config", "opencode", "command");
|
|
25146
|
+
const opencodeProjectDir = join40(process.cwd(), ".opencode", "command");
|
|
24994
25147
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
24995
25148
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
24996
25149
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -25123,8 +25276,8 @@ var SkillFrontmatterSchema = exports_external.object({
|
|
|
25123
25276
|
});
|
|
25124
25277
|
// src/tools/skill/tools.ts
|
|
25125
25278
|
import { existsSync as existsSync34, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
|
|
25126
|
-
import { homedir as
|
|
25127
|
-
import { join as
|
|
25279
|
+
import { homedir as homedir17 } from "os";
|
|
25280
|
+
import { join as join41, basename as basename4 } from "path";
|
|
25128
25281
|
function parseSkillFrontmatter(data) {
|
|
25129
25282
|
return {
|
|
25130
25283
|
name: typeof data.name === "string" ? data.name : "",
|
|
@@ -25143,10 +25296,10 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
25143
25296
|
for (const entry of entries) {
|
|
25144
25297
|
if (entry.name.startsWith("."))
|
|
25145
25298
|
continue;
|
|
25146
|
-
const skillPath =
|
|
25299
|
+
const skillPath = join41(skillsDir, entry.name);
|
|
25147
25300
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
25148
25301
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25149
|
-
const skillMdPath =
|
|
25302
|
+
const skillMdPath = join41(resolvedPath, "SKILL.md");
|
|
25150
25303
|
if (!existsSync34(skillMdPath))
|
|
25151
25304
|
continue;
|
|
25152
25305
|
try {
|
|
@@ -25165,8 +25318,8 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
25165
25318
|
return skills;
|
|
25166
25319
|
}
|
|
25167
25320
|
function discoverSkillsSync() {
|
|
25168
|
-
const userSkillsDir =
|
|
25169
|
-
const projectSkillsDir =
|
|
25321
|
+
const userSkillsDir = join41(homedir17(), ".claude", "skills");
|
|
25322
|
+
const projectSkillsDir = join41(process.cwd(), ".claude", "skills");
|
|
25170
25323
|
const userSkills = discoverSkillsFromDir(userSkillsDir, "user");
|
|
25171
25324
|
const projectSkills = discoverSkillsFromDir(projectSkillsDir, "project");
|
|
25172
25325
|
return [...projectSkills, ...userSkills];
|
|
@@ -25176,7 +25329,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
|
|
|
25176
25329
|
`);
|
|
25177
25330
|
async function parseSkillMd(skillPath) {
|
|
25178
25331
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25179
|
-
const skillMdPath =
|
|
25332
|
+
const skillMdPath = join41(resolvedPath, "SKILL.md");
|
|
25180
25333
|
if (!existsSync34(skillMdPath)) {
|
|
25181
25334
|
return null;
|
|
25182
25335
|
}
|
|
@@ -25192,9 +25345,9 @@ async function parseSkillMd(skillPath) {
|
|
|
25192
25345
|
allowedTools: frontmatter2["allowed-tools"],
|
|
25193
25346
|
metadata: frontmatter2.metadata
|
|
25194
25347
|
};
|
|
25195
|
-
const referencesDir =
|
|
25196
|
-
const scriptsDir =
|
|
25197
|
-
const assetsDir =
|
|
25348
|
+
const referencesDir = join41(resolvedPath, "references");
|
|
25349
|
+
const scriptsDir = join41(resolvedPath, "scripts");
|
|
25350
|
+
const assetsDir = join41(resolvedPath, "assets");
|
|
25198
25351
|
const references = existsSync34(referencesDir) ? readdirSync10(referencesDir).filter((f) => !f.startsWith(".")) : [];
|
|
25199
25352
|
const scripts = existsSync34(scriptsDir) ? readdirSync10(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
|
|
25200
25353
|
const assets = existsSync34(assetsDir) ? readdirSync10(assetsDir).filter((f) => !f.startsWith(".")) : [];
|
|
@@ -25221,7 +25374,7 @@ async function discoverSkillsFromDirAsync(skillsDir) {
|
|
|
25221
25374
|
for (const entry of entries) {
|
|
25222
25375
|
if (entry.name.startsWith("."))
|
|
25223
25376
|
continue;
|
|
25224
|
-
const skillPath =
|
|
25377
|
+
const skillPath = join41(skillsDir, entry.name);
|
|
25225
25378
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
25226
25379
|
const skillInfo = await parseSkillMd(skillPath);
|
|
25227
25380
|
if (skillInfo) {
|
|
@@ -25232,8 +25385,8 @@ async function discoverSkillsFromDirAsync(skillsDir) {
|
|
|
25232
25385
|
return skills;
|
|
25233
25386
|
}
|
|
25234
25387
|
async function discoverSkills() {
|
|
25235
|
-
const userSkillsDir =
|
|
25236
|
-
const projectSkillsDir =
|
|
25388
|
+
const userSkillsDir = join41(homedir17(), ".claude", "skills");
|
|
25389
|
+
const projectSkillsDir = join41(process.cwd(), ".claude", "skills");
|
|
25237
25390
|
const userSkills = await discoverSkillsFromDirAsync(userSkillsDir);
|
|
25238
25391
|
const projectSkills = await discoverSkillsFromDirAsync(projectSkillsDir);
|
|
25239
25392
|
return [...projectSkills, ...userSkills];
|
|
@@ -25262,7 +25415,7 @@ async function loadSkillWithReferences(skill, includeRefs) {
|
|
|
25262
25415
|
const referencesLoaded = [];
|
|
25263
25416
|
if (includeRefs && skill.references.length > 0) {
|
|
25264
25417
|
for (const ref of skill.references) {
|
|
25265
|
-
const refPath =
|
|
25418
|
+
const refPath = join41(skill.path, "references", ref);
|
|
25266
25419
|
try {
|
|
25267
25420
|
let content = readFileSync21(refPath, "utf-8");
|
|
25268
25421
|
content = await resolveCommandsInText(content);
|
|
@@ -25355,17 +25508,33 @@ Try a different skill name.`;
|
|
|
25355
25508
|
});
|
|
25356
25509
|
// src/tools/interactive-bash/constants.ts
|
|
25357
25510
|
var DEFAULT_TIMEOUT_MS4 = 60000;
|
|
25511
|
+
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
25512
|
+
"capture-pane",
|
|
25513
|
+
"capturep",
|
|
25514
|
+
"save-buffer",
|
|
25515
|
+
"saveb",
|
|
25516
|
+
"show-buffer",
|
|
25517
|
+
"showb",
|
|
25518
|
+
"pipe-pane",
|
|
25519
|
+
"pipep"
|
|
25520
|
+
];
|
|
25358
25521
|
var INTERACTIVE_BASH_DESCRIPTION = `Execute tmux commands for interactive terminal session management.
|
|
25359
25522
|
|
|
25360
|
-
Use session names following the pattern "omo-{name}" for automatic tracking
|
|
25523
|
+
Use session names following the pattern "omo-{name}" for automatic tracking.
|
|
25524
|
+
|
|
25525
|
+
BLOCKED COMMANDS (use bash tool instead):
|
|
25526
|
+
- capture-pane / capturep: Use bash to read output files or pipe output
|
|
25527
|
+
- save-buffer / saveb: Use bash to save content to files
|
|
25528
|
+
- show-buffer / showb: Use bash to read buffer content
|
|
25529
|
+
- pipe-pane / pipep: Use bash for piping output`;
|
|
25361
25530
|
|
|
25362
25531
|
// src/tools/interactive-bash/utils.ts
|
|
25363
25532
|
var {spawn: spawn9 } = globalThis.Bun;
|
|
25364
25533
|
var tmuxPath = null;
|
|
25365
25534
|
var initPromise3 = null;
|
|
25366
25535
|
async function findTmuxPath() {
|
|
25367
|
-
const
|
|
25368
|
-
const cmd =
|
|
25536
|
+
const isWindows2 = process.platform === "win32";
|
|
25537
|
+
const cmd = isWindows2 ? "where" : "which";
|
|
25369
25538
|
try {
|
|
25370
25539
|
const proc = spawn9([cmd, "tmux"], {
|
|
25371
25540
|
stdout: "pipe",
|
|
@@ -25461,6 +25630,10 @@ var interactive_bash = tool({
|
|
|
25461
25630
|
if (parts.length === 0) {
|
|
25462
25631
|
return "Error: Empty tmux command";
|
|
25463
25632
|
}
|
|
25633
|
+
const subcommand = parts[0].toLowerCase();
|
|
25634
|
+
if (BLOCKED_TMUX_SUBCOMMANDS.includes(subcommand)) {
|
|
25635
|
+
return `Error: '${parts[0]}' is blocked. Use bash tool instead for capturing/printing terminal output.`;
|
|
25636
|
+
}
|
|
25464
25637
|
const proc = Bun.spawn([tmuxPath2, ...parts], {
|
|
25465
25638
|
stdout: "pipe",
|
|
25466
25639
|
stderr: "pipe"
|
|
@@ -26041,15 +26214,15 @@ var builtinTools = {
|
|
|
26041
26214
|
};
|
|
26042
26215
|
// src/features/background-agent/manager.ts
|
|
26043
26216
|
import { existsSync as existsSync35, readdirSync as readdirSync11 } from "fs";
|
|
26044
|
-
import { join as
|
|
26217
|
+
import { join as join42 } from "path";
|
|
26045
26218
|
function getMessageDir3(sessionID) {
|
|
26046
26219
|
if (!existsSync35(MESSAGE_STORAGE))
|
|
26047
26220
|
return null;
|
|
26048
|
-
const directPath =
|
|
26221
|
+
const directPath = join42(MESSAGE_STORAGE, sessionID);
|
|
26049
26222
|
if (existsSync35(directPath))
|
|
26050
26223
|
return directPath;
|
|
26051
26224
|
for (const dir of readdirSync11(MESSAGE_STORAGE)) {
|
|
26052
|
-
const sessionPath =
|
|
26225
|
+
const sessionPath = join42(MESSAGE_STORAGE, dir, sessionID);
|
|
26053
26226
|
if (existsSync35(sessionPath))
|
|
26054
26227
|
return sessionPath;
|
|
26055
26228
|
}
|
|
@@ -26106,7 +26279,6 @@ class BackgroundManager {
|
|
|
26106
26279
|
agent: input.agent,
|
|
26107
26280
|
tools: {
|
|
26108
26281
|
task: false,
|
|
26109
|
-
call_omo_agent: false,
|
|
26110
26282
|
background_task: false
|
|
26111
26283
|
},
|
|
26112
26284
|
parts: [{ type: "text", text: input.prompt }]
|
|
@@ -26149,6 +26321,20 @@ class BackgroundManager {
|
|
|
26149
26321
|
}
|
|
26150
26322
|
return;
|
|
26151
26323
|
}
|
|
26324
|
+
async checkSessionTodos(sessionID) {
|
|
26325
|
+
try {
|
|
26326
|
+
const response2 = await this.client.session.todo({
|
|
26327
|
+
path: { id: sessionID }
|
|
26328
|
+
});
|
|
26329
|
+
const todos = response2.data ?? response2;
|
|
26330
|
+
if (!todos || todos.length === 0)
|
|
26331
|
+
return false;
|
|
26332
|
+
const incomplete = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
|
|
26333
|
+
return incomplete.length > 0;
|
|
26334
|
+
} catch {
|
|
26335
|
+
return false;
|
|
26336
|
+
}
|
|
26337
|
+
}
|
|
26152
26338
|
handleEvent(event) {
|
|
26153
26339
|
const props = event.properties;
|
|
26154
26340
|
if (event.type === "message.part.updated") {
|
|
@@ -26180,11 +26366,17 @@ class BackgroundManager {
|
|
|
26180
26366
|
const task = this.findBySession(sessionID);
|
|
26181
26367
|
if (!task || task.status !== "running")
|
|
26182
26368
|
return;
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
|
|
26369
|
+
this.checkSessionTodos(sessionID).then((hasIncompleteTodos2) => {
|
|
26370
|
+
if (hasIncompleteTodos2) {
|
|
26371
|
+
log("[background-agent] Task has incomplete todos, waiting for todo-continuation:", task.id);
|
|
26372
|
+
return;
|
|
26373
|
+
}
|
|
26374
|
+
task.status = "completed";
|
|
26375
|
+
task.completedAt = new Date;
|
|
26376
|
+
this.markForNotification(task);
|
|
26377
|
+
this.notifyParentSession(task);
|
|
26378
|
+
log("[background-agent] Task completed via session.idle event:", task.id);
|
|
26379
|
+
});
|
|
26188
26380
|
}
|
|
26189
26381
|
if (event.type === "session.deleted") {
|
|
26190
26382
|
const info = props?.info;
|
|
@@ -26304,6 +26496,11 @@ class BackgroundManager {
|
|
|
26304
26496
|
continue;
|
|
26305
26497
|
}
|
|
26306
26498
|
if (sessionStatus.type === "idle") {
|
|
26499
|
+
const hasIncompleteTodos2 = await this.checkSessionTodos(task.sessionID);
|
|
26500
|
+
if (hasIncompleteTodos2) {
|
|
26501
|
+
log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
|
|
26502
|
+
continue;
|
|
26503
|
+
}
|
|
26307
26504
|
task.status = "completed";
|
|
26308
26505
|
task.completedAt = new Date;
|
|
26309
26506
|
this.markForNotification(task);
|