oh-my-opencode 0.1.24 → 0.1.25
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.ko.md +5 -0
- package/README.md +5 -1
- package/dist/hooks/session-recovery.d.ts +1 -1
- package/dist/index.js +249 -172
- package/dist/tools/lsp/utils.d.ts +2 -2
- package/package.json +2 -1
package/README.ko.md
CHANGED
|
@@ -136,6 +136,11 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
|
|
136
136
|
- **Context Window Monitor**: [컨텍스트 윈도우 불안 관리](https://agentic-patterns.com/patterns/context-window-anxiety-management/) 패턴을 구현합니다.
|
|
137
137
|
- 사용량이 70%를 넘으면 에이전트에게 아직 토큰이 충분하다고 상기시켜, 급하게 불완전한 작업을 하는 것을 완화합니다.
|
|
138
138
|
- **Session Notification**: 에이전트가 작업을 마치면 OS 네이티브 알림을 보냅니다 (macOS, Linux, Windows).
|
|
139
|
+
- **Session Recovery**: API 에러로부터 자동으로 복구하여 세션 안정성을 보장합니다. 네 가지 시나리오를 처리합니다:
|
|
140
|
+
- **Tool Result Missing**: `tool_use` 블록이 있지만 `tool_result`가 없을 때 (ESC 인터럽트) → "cancelled" tool result 주입
|
|
141
|
+
- **Thinking Block Order**: thinking 블록이 첫 번째여야 하는데 아닐 때 → 빈 thinking 블록 추가
|
|
142
|
+
- **Thinking Disabled Violation**: thinking 이 비활성화인데 thinking 블록이 있을 때 → thinking 블록 제거
|
|
143
|
+
- **Empty Content Message**: 메시지가 thinking/meta 블록만 있고 실제 내용이 없을 때 → 파일시스템을 통해 "(interrupted)" 텍스트 주입
|
|
139
144
|
- **Comment Checker**: 코드 수정 후 불필요한 주석을 감지하여 보고합니다. BDD 패턴, 지시어, 독스트링 등 유효한 주석은 똑똑하게 제외하고, AI가 남긴 흔적을 제거하여 코드를 깨끗하게 유지합니다.
|
|
140
145
|
|
|
141
146
|
### Agents
|
package/README.md
CHANGED
|
@@ -132,7 +132,11 @@ I believe in the right tool for the job. For your wallet's sake, use CLIProxyAPI
|
|
|
132
132
|
- **Todo Continuation Enforcer**: Forces the agent to complete all tasks before exiting. Eliminates the common LLM issue of "giving up halfway".
|
|
133
133
|
- **Context Window Monitor**: Implements [Context Window Anxiety Management](https://agentic-patterns.com/patterns/context-window-anxiety-management/). When context usage exceeds 70%, it reminds the agent that resources are sufficient, preventing rushed or low-quality output.
|
|
134
134
|
- **Session Notification**: Sends a native OS notification when the job is done (macOS, Linux, Windows).
|
|
135
|
-
- **Session Recovery**: Automatically recovers from API errors
|
|
135
|
+
- **Session Recovery**: Automatically recovers from API errors, ensuring session stability. Handles four scenarios:
|
|
136
|
+
- **Tool Result Missing**: When `tool_use` block exists without `tool_result` (ESC interrupt) → injects "cancelled" tool results
|
|
137
|
+
- **Thinking Block Order**: When thinking block must be first but isn't → prepends empty thinking block
|
|
138
|
+
- **Thinking Disabled Violation**: When thinking blocks exist but thinking is disabled → strips thinking blocks
|
|
139
|
+
- **Empty Content Message**: When message has only thinking/meta blocks without actual content → injects "(interrupted)" text via filesystem
|
|
136
140
|
- **Comment Checker**: Detects and reports unnecessary comments after code modifications. Smartly ignores valid patterns (BDD, directives, docstrings, shebangs) to keep the codebase clean from AI-generated artifacts.
|
|
137
141
|
|
|
138
142
|
### Agents
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Recovery: strip thinking/redacted_thinking blocks
|
|
13
13
|
*
|
|
14
14
|
* 4. Empty content message (non-empty content required)
|
|
15
|
-
* - Recovery:
|
|
15
|
+
* - Recovery: inject text part directly via filesystem
|
|
16
16
|
*/
|
|
17
17
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
18
18
|
interface MessageInfo {
|
package/dist/index.js
CHANGED
|
@@ -790,6 +790,32 @@ ${CONTEXT_REMINDER}
|
|
|
790
790
|
};
|
|
791
791
|
}
|
|
792
792
|
// src/hooks/session-recovery.ts
|
|
793
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
794
|
+
import { join } from "path";
|
|
795
|
+
|
|
796
|
+
// node_modules/xdg-basedir/index.js
|
|
797
|
+
import os from "os";
|
|
798
|
+
import path from "path";
|
|
799
|
+
var homeDirectory = os.homedir();
|
|
800
|
+
var { env } = process;
|
|
801
|
+
var xdgData = env.XDG_DATA_HOME || (homeDirectory ? path.join(homeDirectory, ".local", "share") : undefined);
|
|
802
|
+
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path.join(homeDirectory, ".config") : undefined);
|
|
803
|
+
var xdgState = env.XDG_STATE_HOME || (homeDirectory ? path.join(homeDirectory, ".local", "state") : undefined);
|
|
804
|
+
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ? path.join(homeDirectory, ".cache") : undefined);
|
|
805
|
+
var xdgRuntime = env.XDG_RUNTIME_DIR || undefined;
|
|
806
|
+
var xdgDataDirectories = (env.XDG_DATA_DIRS || "/usr/local/share/:/usr/share/").split(":");
|
|
807
|
+
if (xdgData) {
|
|
808
|
+
xdgDataDirectories.unshift(xdgData);
|
|
809
|
+
}
|
|
810
|
+
var xdgConfigDirectories = (env.XDG_CONFIG_DIRS || "/etc/xdg").split(":");
|
|
811
|
+
if (xdgConfig) {
|
|
812
|
+
xdgConfigDirectories.unshift(xdgConfig);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/hooks/session-recovery.ts
|
|
816
|
+
var OPENCODE_STORAGE = join(xdgData ?? "", "opencode", "storage");
|
|
817
|
+
var MESSAGE_STORAGE = join(OPENCODE_STORAGE, "message");
|
|
818
|
+
var PART_STORAGE = join(OPENCODE_STORAGE, "part");
|
|
793
819
|
function getErrorMessage(error) {
|
|
794
820
|
if (!error)
|
|
795
821
|
return "";
|
|
@@ -894,80 +920,121 @@ async function recoverThinkingDisabledViolation(client, sessionID, failedAssista
|
|
|
894
920
|
return false;
|
|
895
921
|
}
|
|
896
922
|
var THINKING_TYPES = new Set(["thinking", "redacted_thinking", "reasoning"]);
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
return
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
923
|
+
var META_TYPES = new Set(["step-start", "step-finish"]);
|
|
924
|
+
function generatePartId() {
|
|
925
|
+
const timestamp = Date.now().toString(16);
|
|
926
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
927
|
+
return `prt_${timestamp}${random}`;
|
|
928
|
+
}
|
|
929
|
+
function getMessageDir(sessionID) {
|
|
930
|
+
const projectHash = readdirSync(MESSAGE_STORAGE).find((dir) => {
|
|
931
|
+
const sessionDir = join(MESSAGE_STORAGE, dir);
|
|
932
|
+
try {
|
|
933
|
+
return readdirSync(sessionDir).some((f) => f.includes(sessionID.replace("ses_", "")));
|
|
934
|
+
} catch {
|
|
905
935
|
return false;
|
|
906
|
-
|
|
907
|
-
return true;
|
|
908
|
-
if (p.type === "tool_use" && p.id)
|
|
909
|
-
return true;
|
|
910
|
-
if (p.type === "tool_result")
|
|
911
|
-
return true;
|
|
912
|
-
return false;
|
|
936
|
+
}
|
|
913
937
|
});
|
|
938
|
+
if (projectHash) {
|
|
939
|
+
return join(MESSAGE_STORAGE, projectHash, sessionID);
|
|
940
|
+
}
|
|
941
|
+
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
|
942
|
+
const sessionPath = join(MESSAGE_STORAGE, dir, sessionID);
|
|
943
|
+
if (existsSync(sessionPath)) {
|
|
944
|
+
return sessionPath;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return "";
|
|
914
948
|
}
|
|
915
|
-
function
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
949
|
+
function readMessagesFromStorage(sessionID) {
|
|
950
|
+
const messageDir = getMessageDir(sessionID);
|
|
951
|
+
if (!messageDir || !existsSync(messageDir))
|
|
952
|
+
return [];
|
|
953
|
+
const messages = [];
|
|
954
|
+
for (const file of readdirSync(messageDir)) {
|
|
955
|
+
if (!file.endsWith(".json"))
|
|
956
|
+
continue;
|
|
957
|
+
try {
|
|
958
|
+
const content = readFileSync(join(messageDir, file), "utf-8");
|
|
959
|
+
messages.push(JSON.parse(content));
|
|
960
|
+
} catch {
|
|
921
961
|
continue;
|
|
922
|
-
if (!hasNonEmptyOutput(msg)) {
|
|
923
|
-
return msg;
|
|
924
962
|
}
|
|
925
963
|
}
|
|
926
|
-
return
|
|
964
|
+
return messages.sort((a, b) => a.id.localeCompare(b.id));
|
|
927
965
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
const existingParts = emptyMsg.parts || [];
|
|
942
|
-
const hasOnlyThinkingOrMeta = existingParts.length > 0 && existingParts.every((p) => THINKING_TYPES.has(p.type) || p.type === "step-start" || p.type === "step-finish");
|
|
943
|
-
if (hasOnlyThinkingOrMeta) {
|
|
944
|
-
const strippedParts = [{ type: "text", text: "(interrupted)" }];
|
|
945
|
-
try {
|
|
946
|
-
await client.message?.update?.({
|
|
947
|
-
path: { id: messageID },
|
|
948
|
-
body: { parts: strippedParts }
|
|
949
|
-
});
|
|
950
|
-
return true;
|
|
951
|
-
} catch {}
|
|
952
|
-
try {
|
|
953
|
-
await client.session.patch?.({
|
|
954
|
-
path: { id: sessionID },
|
|
955
|
-
body: { messageID, parts: strippedParts }
|
|
956
|
-
});
|
|
957
|
-
return true;
|
|
958
|
-
} catch {}
|
|
966
|
+
function readPartsFromStorage(messageID) {
|
|
967
|
+
const partDir = join(PART_STORAGE, messageID);
|
|
968
|
+
if (!existsSync(partDir))
|
|
969
|
+
return [];
|
|
970
|
+
const parts = [];
|
|
971
|
+
for (const file of readdirSync(partDir)) {
|
|
972
|
+
if (!file.endsWith(".json"))
|
|
973
|
+
continue;
|
|
974
|
+
try {
|
|
975
|
+
const content = readFileSync(join(partDir, file), "utf-8");
|
|
976
|
+
parts.push(JSON.parse(content));
|
|
977
|
+
} catch {
|
|
978
|
+
continue;
|
|
959
979
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
980
|
+
}
|
|
981
|
+
return parts;
|
|
982
|
+
}
|
|
983
|
+
function injectTextPartToStorage(sessionID, messageID, text) {
|
|
984
|
+
const partDir = join(PART_STORAGE, messageID);
|
|
985
|
+
if (!existsSync(partDir)) {
|
|
986
|
+
mkdirSync(partDir, { recursive: true });
|
|
987
|
+
}
|
|
988
|
+
const partId = generatePartId();
|
|
989
|
+
const part = {
|
|
990
|
+
id: partId,
|
|
991
|
+
sessionID,
|
|
992
|
+
messageID,
|
|
993
|
+
type: "text",
|
|
994
|
+
text
|
|
995
|
+
};
|
|
996
|
+
try {
|
|
997
|
+
writeFileSync(join(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
966
998
|
return true;
|
|
967
999
|
} catch {
|
|
968
1000
|
return false;
|
|
969
1001
|
}
|
|
970
1002
|
}
|
|
1003
|
+
function findEmptyContentMessageFromStorage(sessionID) {
|
|
1004
|
+
const messages = readMessagesFromStorage(sessionID);
|
|
1005
|
+
for (let i = 0;i < messages.length; i++) {
|
|
1006
|
+
const msg = messages[i];
|
|
1007
|
+
if (msg.role !== "assistant")
|
|
1008
|
+
continue;
|
|
1009
|
+
const isLastMessage = i === messages.length - 1;
|
|
1010
|
+
if (isLastMessage)
|
|
1011
|
+
continue;
|
|
1012
|
+
const parts = readPartsFromStorage(msg.id);
|
|
1013
|
+
const hasContent = parts.some((p) => {
|
|
1014
|
+
if (THINKING_TYPES.has(p.type))
|
|
1015
|
+
return false;
|
|
1016
|
+
if (META_TYPES.has(p.type))
|
|
1017
|
+
return false;
|
|
1018
|
+
if (p.type === "text" && p.text?.trim())
|
|
1019
|
+
return true;
|
|
1020
|
+
if (p.type === "tool_use")
|
|
1021
|
+
return true;
|
|
1022
|
+
if (p.type === "tool_result")
|
|
1023
|
+
return true;
|
|
1024
|
+
return false;
|
|
1025
|
+
});
|
|
1026
|
+
if (!hasContent && parts.length > 0) {
|
|
1027
|
+
return msg.id;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
async function recoverEmptyContentMessage(_client, sessionID, failedAssistantMsg, _directory) {
|
|
1033
|
+
const emptyMessageID = findEmptyContentMessageFromStorage(sessionID) || failedAssistantMsg.info?.id;
|
|
1034
|
+
if (!emptyMessageID)
|
|
1035
|
+
return false;
|
|
1036
|
+
return injectTextPartToStorage(sessionID, emptyMessageID, "(interrupted)");
|
|
1037
|
+
}
|
|
971
1038
|
async function fallbackRevertStrategy(client, sessionID, failedAssistantMsg, directory) {
|
|
972
1039
|
const parentMsgID = failedAssistantMsg.info?.parentID;
|
|
973
1040
|
const messagesResp = await client.session.messages({
|
|
@@ -1093,14 +1160,14 @@ function createSessionRecoveryHook(ctx) {
|
|
|
1093
1160
|
// src/hooks/comment-checker/cli.ts
|
|
1094
1161
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
1095
1162
|
import { createRequire as createRequire2 } from "module";
|
|
1096
|
-
import { dirname, join as
|
|
1097
|
-
import { existsSync as
|
|
1163
|
+
import { dirname, join as join3 } from "path";
|
|
1164
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1098
1165
|
import * as fs from "fs";
|
|
1099
1166
|
|
|
1100
1167
|
// src/hooks/comment-checker/downloader.ts
|
|
1101
1168
|
var {spawn } = globalThis.Bun;
|
|
1102
|
-
import { existsSync, mkdirSync, chmodSync, unlinkSync, appendFileSync } from "fs";
|
|
1103
|
-
import { join } from "path";
|
|
1169
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, unlinkSync, appendFileSync } from "fs";
|
|
1170
|
+
import { join as join2 } from "path";
|
|
1104
1171
|
import { homedir } from "os";
|
|
1105
1172
|
import { createRequire } from "module";
|
|
1106
1173
|
var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
@@ -1121,16 +1188,16 @@ var PLATFORM_MAP = {
|
|
|
1121
1188
|
"win32-x64": { os: "windows", arch: "amd64", ext: "zip" }
|
|
1122
1189
|
};
|
|
1123
1190
|
function getCacheDir() {
|
|
1124
|
-
const
|
|
1125
|
-
const base =
|
|
1126
|
-
return
|
|
1191
|
+
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
1192
|
+
const base = xdgCache2 || join2(homedir(), ".cache");
|
|
1193
|
+
return join2(base, "oh-my-opencode", "bin");
|
|
1127
1194
|
}
|
|
1128
1195
|
function getBinaryName() {
|
|
1129
1196
|
return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
|
|
1130
1197
|
}
|
|
1131
1198
|
function getCachedBinaryPath() {
|
|
1132
|
-
const binaryPath =
|
|
1133
|
-
return
|
|
1199
|
+
const binaryPath = join2(getCacheDir(), getBinaryName());
|
|
1200
|
+
return existsSync2(binaryPath) ? binaryPath : null;
|
|
1134
1201
|
}
|
|
1135
1202
|
function getPackageVersion() {
|
|
1136
1203
|
try {
|
|
@@ -1177,26 +1244,26 @@ async function downloadCommentChecker() {
|
|
|
1177
1244
|
}
|
|
1178
1245
|
const cacheDir = getCacheDir();
|
|
1179
1246
|
const binaryName = getBinaryName();
|
|
1180
|
-
const binaryPath =
|
|
1181
|
-
if (
|
|
1247
|
+
const binaryPath = join2(cacheDir, binaryName);
|
|
1248
|
+
if (existsSync2(binaryPath)) {
|
|
1182
1249
|
debugLog("Binary already cached at:", binaryPath);
|
|
1183
1250
|
return binaryPath;
|
|
1184
1251
|
}
|
|
1185
1252
|
const version = getPackageVersion();
|
|
1186
|
-
const { os, arch, ext } = platformInfo;
|
|
1187
|
-
const assetName = `comment-checker_v${version}_${
|
|
1253
|
+
const { os: os2, arch, ext } = platformInfo;
|
|
1254
|
+
const assetName = `comment-checker_v${version}_${os2}_${arch}.${ext}`;
|
|
1188
1255
|
const downloadUrl = `https://github.com/${REPO}/releases/download/v${version}/${assetName}`;
|
|
1189
1256
|
debugLog(`Downloading from: ${downloadUrl}`);
|
|
1190
1257
|
console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
|
|
1191
1258
|
try {
|
|
1192
|
-
if (!
|
|
1193
|
-
|
|
1259
|
+
if (!existsSync2(cacheDir)) {
|
|
1260
|
+
mkdirSync2(cacheDir, { recursive: true });
|
|
1194
1261
|
}
|
|
1195
1262
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
1196
1263
|
if (!response.ok) {
|
|
1197
1264
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1198
1265
|
}
|
|
1199
|
-
const archivePath =
|
|
1266
|
+
const archivePath = join2(cacheDir, assetName);
|
|
1200
1267
|
const arrayBuffer = await response.arrayBuffer();
|
|
1201
1268
|
await Bun.write(archivePath, arrayBuffer);
|
|
1202
1269
|
debugLog(`Downloaded archive to: ${archivePath}`);
|
|
@@ -1205,10 +1272,10 @@ async function downloadCommentChecker() {
|
|
|
1205
1272
|
} else {
|
|
1206
1273
|
await extractZip(archivePath, cacheDir);
|
|
1207
1274
|
}
|
|
1208
|
-
if (
|
|
1275
|
+
if (existsSync2(archivePath)) {
|
|
1209
1276
|
unlinkSync(archivePath);
|
|
1210
1277
|
}
|
|
1211
|
-
if (process.platform !== "win32" &&
|
|
1278
|
+
if (process.platform !== "win32" && existsSync2(binaryPath)) {
|
|
1212
1279
|
chmodSync(binaryPath, 493);
|
|
1213
1280
|
}
|
|
1214
1281
|
debugLog(`Successfully downloaded binary to: ${binaryPath}`);
|
|
@@ -1261,8 +1328,8 @@ function findCommentCheckerPathSync() {
|
|
|
1261
1328
|
const require2 = createRequire2(import.meta.url);
|
|
1262
1329
|
const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
|
|
1263
1330
|
const cliDir = dirname(cliPkgPath);
|
|
1264
|
-
const binaryPath =
|
|
1265
|
-
if (
|
|
1331
|
+
const binaryPath = join3(cliDir, "bin", binaryName);
|
|
1332
|
+
if (existsSync3(binaryPath)) {
|
|
1266
1333
|
debugLog2("found binary in main package:", binaryPath);
|
|
1267
1334
|
return binaryPath;
|
|
1268
1335
|
}
|
|
@@ -1275,8 +1342,8 @@ function findCommentCheckerPathSync() {
|
|
|
1275
1342
|
const require2 = createRequire2(import.meta.url);
|
|
1276
1343
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
1277
1344
|
const pkgDir = dirname(pkgPath);
|
|
1278
|
-
const binaryPath =
|
|
1279
|
-
if (
|
|
1345
|
+
const binaryPath = join3(pkgDir, "bin", binaryName);
|
|
1346
|
+
if (existsSync3(binaryPath)) {
|
|
1280
1347
|
debugLog2("found binary in platform package:", binaryPath);
|
|
1281
1348
|
return binaryPath;
|
|
1282
1349
|
}
|
|
@@ -1289,10 +1356,10 @@ function findCommentCheckerPathSync() {
|
|
|
1289
1356
|
"/opt/homebrew/bin/comment-checker",
|
|
1290
1357
|
"/usr/local/bin/comment-checker"
|
|
1291
1358
|
];
|
|
1292
|
-
for (const
|
|
1293
|
-
if (
|
|
1294
|
-
debugLog2("found binary via homebrew:",
|
|
1295
|
-
return
|
|
1359
|
+
for (const path2 of homebrewPaths) {
|
|
1360
|
+
if (existsSync3(path2)) {
|
|
1361
|
+
debugLog2("found binary via homebrew:", path2);
|
|
1362
|
+
return path2;
|
|
1296
1363
|
}
|
|
1297
1364
|
}
|
|
1298
1365
|
}
|
|
@@ -1315,7 +1382,7 @@ async function getCommentCheckerPath() {
|
|
|
1315
1382
|
}
|
|
1316
1383
|
initPromise = (async () => {
|
|
1317
1384
|
const syncPath = findCommentCheckerPathSync();
|
|
1318
|
-
if (syncPath &&
|
|
1385
|
+
if (syncPath && existsSync3(syncPath)) {
|
|
1319
1386
|
resolvedCliPath = syncPath;
|
|
1320
1387
|
debugLog2("using sync-resolved path:", syncPath);
|
|
1321
1388
|
return syncPath;
|
|
@@ -1335,8 +1402,8 @@ async function getCommentCheckerPath() {
|
|
|
1335
1402
|
function startBackgroundInit() {
|
|
1336
1403
|
if (!initPromise) {
|
|
1337
1404
|
initPromise = getCommentCheckerPath();
|
|
1338
|
-
initPromise.then((
|
|
1339
|
-
debugLog2("background init complete:",
|
|
1405
|
+
initPromise.then((path2) => {
|
|
1406
|
+
debugLog2("background init complete:", path2 || "no binary");
|
|
1340
1407
|
}).catch((err) => {
|
|
1341
1408
|
debugLog2("background init error:", err);
|
|
1342
1409
|
});
|
|
@@ -1349,7 +1416,7 @@ async function runCommentChecker(input, cliPath) {
|
|
|
1349
1416
|
debugLog2("comment-checker binary not found");
|
|
1350
1417
|
return { hasComments: false, message: "" };
|
|
1351
1418
|
}
|
|
1352
|
-
if (!
|
|
1419
|
+
if (!existsSync3(binaryPath)) {
|
|
1353
1420
|
debugLog2("comment-checker binary does not exist:", binaryPath);
|
|
1354
1421
|
return { hasComments: false, message: "" };
|
|
1355
1422
|
}
|
|
@@ -1383,7 +1450,7 @@ async function runCommentChecker(input, cliPath) {
|
|
|
1383
1450
|
|
|
1384
1451
|
// src/hooks/comment-checker/index.ts
|
|
1385
1452
|
import * as fs2 from "fs";
|
|
1386
|
-
import { existsSync as
|
|
1453
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1387
1454
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
1388
1455
|
var DEBUG_FILE3 = "/tmp/comment-checker-debug.log";
|
|
1389
1456
|
function debugLog3(...args) {
|
|
@@ -1409,8 +1476,8 @@ function createCommentCheckerHooks() {
|
|
|
1409
1476
|
debugLog3("createCommentCheckerHooks called");
|
|
1410
1477
|
startBackgroundInit();
|
|
1411
1478
|
cliPathPromise = getCommentCheckerPath();
|
|
1412
|
-
cliPathPromise.then((
|
|
1413
|
-
debugLog3("CLI path resolved:",
|
|
1479
|
+
cliPathPromise.then((path2) => {
|
|
1480
|
+
debugLog3("CLI path resolved:", path2 || "disabled (no binary)");
|
|
1414
1481
|
}).catch((err) => {
|
|
1415
1482
|
debugLog3("CLI path resolution error:", err);
|
|
1416
1483
|
});
|
|
@@ -1461,7 +1528,7 @@ function createCommentCheckerHooks() {
|
|
|
1461
1528
|
}
|
|
1462
1529
|
try {
|
|
1463
1530
|
const cliPath = await cliPathPromise;
|
|
1464
|
-
if (!cliPath || !
|
|
1531
|
+
if (!cliPath || !existsSync4(cliPath)) {
|
|
1465
1532
|
debugLog3("CLI not available, skipping comment check");
|
|
1466
1533
|
return;
|
|
1467
1534
|
}
|
|
@@ -1710,14 +1777,14 @@ var EXT_TO_LANG = {
|
|
|
1710
1777
|
".yml": "yaml"
|
|
1711
1778
|
};
|
|
1712
1779
|
// src/tools/lsp/config.ts
|
|
1713
|
-
import { existsSync as
|
|
1714
|
-
import { join as
|
|
1780
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
1781
|
+
import { join as join4 } from "path";
|
|
1715
1782
|
import { homedir as homedir2 } from "os";
|
|
1716
|
-
function loadJsonFile(
|
|
1717
|
-
if (!
|
|
1783
|
+
function loadJsonFile(path2) {
|
|
1784
|
+
if (!existsSync5(path2))
|
|
1718
1785
|
return null;
|
|
1719
1786
|
try {
|
|
1720
|
-
return JSON.parse(
|
|
1787
|
+
return JSON.parse(readFileSync2(path2, "utf-8"));
|
|
1721
1788
|
} catch {
|
|
1722
1789
|
return null;
|
|
1723
1790
|
}
|
|
@@ -1725,9 +1792,9 @@ function loadJsonFile(path) {
|
|
|
1725
1792
|
function getConfigPaths() {
|
|
1726
1793
|
const cwd = process.cwd();
|
|
1727
1794
|
return {
|
|
1728
|
-
project:
|
|
1729
|
-
user:
|
|
1730
|
-
opencode:
|
|
1795
|
+
project: join4(cwd, ".opencode", "oh-my-opencode.json"),
|
|
1796
|
+
user: join4(homedir2(), ".config", "opencode", "oh-my-opencode.json"),
|
|
1797
|
+
opencode: join4(homedir2(), ".config", "opencode", "opencode.json")
|
|
1731
1798
|
};
|
|
1732
1799
|
}
|
|
1733
1800
|
function loadAllConfigs() {
|
|
@@ -1820,7 +1887,7 @@ function isServerInstalled(command) {
|
|
|
1820
1887
|
const pathEnv = process.env.PATH || "";
|
|
1821
1888
|
const paths = pathEnv.split(":");
|
|
1822
1889
|
for (const p of paths) {
|
|
1823
|
-
if (
|
|
1890
|
+
if (existsSync5(join4(p, cmd))) {
|
|
1824
1891
|
return true;
|
|
1825
1892
|
}
|
|
1826
1893
|
}
|
|
@@ -1870,7 +1937,7 @@ function getAllServers() {
|
|
|
1870
1937
|
}
|
|
1871
1938
|
// src/tools/lsp/client.ts
|
|
1872
1939
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
1873
|
-
import { readFileSync as
|
|
1940
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1874
1941
|
import { extname, resolve } from "path";
|
|
1875
1942
|
class LSPServerManager {
|
|
1876
1943
|
static instance;
|
|
@@ -2257,7 +2324,7 @@ ${msg}`);
|
|
|
2257
2324
|
const absPath = resolve(filePath);
|
|
2258
2325
|
if (this.openedFiles.has(absPath))
|
|
2259
2326
|
return;
|
|
2260
|
-
const text =
|
|
2327
|
+
const text = readFileSync3(absPath, "utf-8");
|
|
2261
2328
|
const ext = extname(absPath);
|
|
2262
2329
|
const languageId = getLanguageId(ext);
|
|
2263
2330
|
this.notify("textDocument/didOpen", {
|
|
@@ -2364,16 +2431,16 @@ ${msg}`);
|
|
|
2364
2431
|
}
|
|
2365
2432
|
// src/tools/lsp/utils.ts
|
|
2366
2433
|
import { extname as extname2, resolve as resolve2 } from "path";
|
|
2367
|
-
import { existsSync as
|
|
2434
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
2368
2435
|
function findWorkspaceRoot(filePath) {
|
|
2369
2436
|
let dir = resolve2(filePath);
|
|
2370
|
-
if (!
|
|
2437
|
+
if (!existsSync6(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
2371
2438
|
dir = __require("path").dirname(dir);
|
|
2372
2439
|
}
|
|
2373
2440
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
2374
2441
|
while (dir !== "/") {
|
|
2375
2442
|
for (const marker of markers) {
|
|
2376
|
-
if (
|
|
2443
|
+
if (existsSync6(__require("path").join(dir, marker))) {
|
|
2377
2444
|
return dir;
|
|
2378
2445
|
}
|
|
2379
2446
|
}
|
|
@@ -2487,12 +2554,22 @@ function formatPrepareRenameResult(result) {
|
|
|
2487
2554
|
if ("defaultBehavior" in result) {
|
|
2488
2555
|
return result.defaultBehavior ? "Rename supported (using default behavior)" : "Cannot rename at this position";
|
|
2489
2556
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2557
|
+
if ("range" in result && result.range) {
|
|
2558
|
+
const startLine = result.range.start.line + 1;
|
|
2559
|
+
const startChar = result.range.start.character;
|
|
2560
|
+
const endLine = result.range.end.line + 1;
|
|
2561
|
+
const endChar = result.range.end.character;
|
|
2562
|
+
const placeholder = result.placeholder ? ` (current: "${result.placeholder}")` : "";
|
|
2563
|
+
return `Rename available at ${startLine}:${startChar}-${endLine}:${endChar}${placeholder}`;
|
|
2564
|
+
}
|
|
2565
|
+
if ("start" in result && "end" in result) {
|
|
2566
|
+
const startLine = result.start.line + 1;
|
|
2567
|
+
const startChar = result.start.character;
|
|
2568
|
+
const endLine = result.end.line + 1;
|
|
2569
|
+
const endChar = result.end.character;
|
|
2570
|
+
return `Rename available at ${startLine}:${startChar}-${endLine}:${endChar}`;
|
|
2571
|
+
}
|
|
2572
|
+
return "Cannot rename at this position";
|
|
2496
2573
|
}
|
|
2497
2574
|
function formatCodeAction(action) {
|
|
2498
2575
|
let result = `[${action.kind || "action"}] ${action.title}`;
|
|
@@ -2521,7 +2598,7 @@ function formatCodeActions(actions) {
|
|
|
2521
2598
|
}
|
|
2522
2599
|
function applyTextEditsToFile(filePath, edits) {
|
|
2523
2600
|
try {
|
|
2524
|
-
let content =
|
|
2601
|
+
let content = readFileSync4(filePath, "utf-8");
|
|
2525
2602
|
const lines = content.split(`
|
|
2526
2603
|
`);
|
|
2527
2604
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -2546,7 +2623,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
2546
2623
|
`));
|
|
2547
2624
|
}
|
|
2548
2625
|
}
|
|
2549
|
-
|
|
2626
|
+
writeFileSync2(filePath, lines.join(`
|
|
2550
2627
|
`), "utf-8");
|
|
2551
2628
|
return { success: true, editCount: edits.length };
|
|
2552
2629
|
} catch (err) {
|
|
@@ -2577,7 +2654,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
2577
2654
|
if (change.kind === "create") {
|
|
2578
2655
|
try {
|
|
2579
2656
|
const filePath = change.uri.replace("file://", "");
|
|
2580
|
-
|
|
2657
|
+
writeFileSync2(filePath, "", "utf-8");
|
|
2581
2658
|
result.filesModified.push(filePath);
|
|
2582
2659
|
} catch (err) {
|
|
2583
2660
|
result.success = false;
|
|
@@ -2587,8 +2664,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
2587
2664
|
try {
|
|
2588
2665
|
const oldPath = change.oldUri.replace("file://", "");
|
|
2589
2666
|
const newPath = change.newUri.replace("file://", "");
|
|
2590
|
-
const content =
|
|
2591
|
-
|
|
2667
|
+
const content = readFileSync4(oldPath, "utf-8");
|
|
2668
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
2592
2669
|
__require("fs").unlinkSync(oldPath);
|
|
2593
2670
|
result.filesModified.push(newPath);
|
|
2594
2671
|
} catch (err) {
|
|
@@ -3368,10 +3445,10 @@ function mergeDefs(...defs) {
|
|
|
3368
3445
|
function cloneDef(schema) {
|
|
3369
3446
|
return mergeDefs(schema._zod.def);
|
|
3370
3447
|
}
|
|
3371
|
-
function getElementAtPath(obj,
|
|
3372
|
-
if (!
|
|
3448
|
+
function getElementAtPath(obj, path2) {
|
|
3449
|
+
if (!path2)
|
|
3373
3450
|
return obj;
|
|
3374
|
-
return
|
|
3451
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
3375
3452
|
}
|
|
3376
3453
|
function promiseAllObject(promisesObj) {
|
|
3377
3454
|
const keys = Object.keys(promisesObj);
|
|
@@ -3730,11 +3807,11 @@ function aborted(x, startIndex = 0) {
|
|
|
3730
3807
|
}
|
|
3731
3808
|
return false;
|
|
3732
3809
|
}
|
|
3733
|
-
function prefixIssues(
|
|
3810
|
+
function prefixIssues(path2, issues) {
|
|
3734
3811
|
return issues.map((iss) => {
|
|
3735
3812
|
var _a;
|
|
3736
3813
|
(_a = iss).path ?? (_a.path = []);
|
|
3737
|
-
iss.path.unshift(
|
|
3814
|
+
iss.path.unshift(path2);
|
|
3738
3815
|
return iss;
|
|
3739
3816
|
});
|
|
3740
3817
|
}
|
|
@@ -3902,7 +3979,7 @@ function treeifyError(error, _mapper) {
|
|
|
3902
3979
|
return issue2.message;
|
|
3903
3980
|
};
|
|
3904
3981
|
const result = { errors: [] };
|
|
3905
|
-
const processError = (error2,
|
|
3982
|
+
const processError = (error2, path2 = []) => {
|
|
3906
3983
|
var _a, _b;
|
|
3907
3984
|
for (const issue2 of error2.issues) {
|
|
3908
3985
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -3912,7 +3989,7 @@ function treeifyError(error, _mapper) {
|
|
|
3912
3989
|
} else if (issue2.code === "invalid_element") {
|
|
3913
3990
|
processError({ issues: issue2.issues }, issue2.path);
|
|
3914
3991
|
} else {
|
|
3915
|
-
const fullpath = [...
|
|
3992
|
+
const fullpath = [...path2, ...issue2.path];
|
|
3916
3993
|
if (fullpath.length === 0) {
|
|
3917
3994
|
result.errors.push(mapper(issue2));
|
|
3918
3995
|
continue;
|
|
@@ -3944,8 +4021,8 @@ function treeifyError(error, _mapper) {
|
|
|
3944
4021
|
}
|
|
3945
4022
|
function toDotPath(_path) {
|
|
3946
4023
|
const segs = [];
|
|
3947
|
-
const
|
|
3948
|
-
for (const seg of
|
|
4024
|
+
const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4025
|
+
for (const seg of path2) {
|
|
3949
4026
|
if (typeof seg === "number")
|
|
3950
4027
|
segs.push(`[${seg}]`);
|
|
3951
4028
|
else if (typeof seg === "symbol")
|
|
@@ -15288,13 +15365,13 @@ var lsp_code_action_resolve = tool({
|
|
|
15288
15365
|
});
|
|
15289
15366
|
// src/tools/ast-grep/constants.ts
|
|
15290
15367
|
import { createRequire as createRequire4 } from "module";
|
|
15291
|
-
import { dirname as dirname2, join as
|
|
15292
|
-
import { existsSync as
|
|
15368
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
15369
|
+
import { existsSync as existsSync8, statSync } from "fs";
|
|
15293
15370
|
|
|
15294
15371
|
// src/tools/ast-grep/downloader.ts
|
|
15295
15372
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
15296
|
-
import { existsSync as
|
|
15297
|
-
import { join as
|
|
15373
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, chmodSync as chmodSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
15374
|
+
import { join as join5 } from "path";
|
|
15298
15375
|
import { homedir as homedir3 } from "os";
|
|
15299
15376
|
import { createRequire as createRequire3 } from "module";
|
|
15300
15377
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -15320,19 +15397,19 @@ var PLATFORM_MAP2 = {
|
|
|
15320
15397
|
function getCacheDir2() {
|
|
15321
15398
|
if (process.platform === "win32") {
|
|
15322
15399
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
15323
|
-
const base2 = localAppData ||
|
|
15324
|
-
return
|
|
15400
|
+
const base2 = localAppData || join5(homedir3(), "AppData", "Local");
|
|
15401
|
+
return join5(base2, "oh-my-opencode", "bin");
|
|
15325
15402
|
}
|
|
15326
|
-
const
|
|
15327
|
-
const base =
|
|
15328
|
-
return
|
|
15403
|
+
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
15404
|
+
const base = xdgCache2 || join5(homedir3(), ".cache");
|
|
15405
|
+
return join5(base, "oh-my-opencode", "bin");
|
|
15329
15406
|
}
|
|
15330
15407
|
function getBinaryName3() {
|
|
15331
15408
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
15332
15409
|
}
|
|
15333
15410
|
function getCachedBinaryPath2() {
|
|
15334
|
-
const binaryPath =
|
|
15335
|
-
return
|
|
15411
|
+
const binaryPath = join5(getCacheDir2(), getBinaryName3());
|
|
15412
|
+
return existsSync7(binaryPath) ? binaryPath : null;
|
|
15336
15413
|
}
|
|
15337
15414
|
async function extractZip2(archivePath, destDir) {
|
|
15338
15415
|
const proc = process.platform === "win32" ? spawn4([
|
|
@@ -15358,30 +15435,30 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
15358
15435
|
}
|
|
15359
15436
|
const cacheDir = getCacheDir2();
|
|
15360
15437
|
const binaryName = getBinaryName3();
|
|
15361
|
-
const binaryPath =
|
|
15362
|
-
if (
|
|
15438
|
+
const binaryPath = join5(cacheDir, binaryName);
|
|
15439
|
+
if (existsSync7(binaryPath)) {
|
|
15363
15440
|
return binaryPath;
|
|
15364
15441
|
}
|
|
15365
|
-
const { arch, os } = platformInfo;
|
|
15366
|
-
const assetName = `app-${arch}-${
|
|
15442
|
+
const { arch, os: os2 } = platformInfo;
|
|
15443
|
+
const assetName = `app-${arch}-${os2}.zip`;
|
|
15367
15444
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
15368
15445
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
15369
15446
|
try {
|
|
15370
|
-
if (!
|
|
15371
|
-
|
|
15447
|
+
if (!existsSync7(cacheDir)) {
|
|
15448
|
+
mkdirSync3(cacheDir, { recursive: true });
|
|
15372
15449
|
}
|
|
15373
15450
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
15374
15451
|
if (!response.ok) {
|
|
15375
15452
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
15376
15453
|
}
|
|
15377
|
-
const archivePath =
|
|
15454
|
+
const archivePath = join5(cacheDir, assetName);
|
|
15378
15455
|
const arrayBuffer = await response.arrayBuffer();
|
|
15379
15456
|
await Bun.write(archivePath, arrayBuffer);
|
|
15380
15457
|
await extractZip2(archivePath, cacheDir);
|
|
15381
|
-
if (
|
|
15458
|
+
if (existsSync7(archivePath)) {
|
|
15382
15459
|
unlinkSync2(archivePath);
|
|
15383
15460
|
}
|
|
15384
|
-
if (process.platform !== "win32" &&
|
|
15461
|
+
if (process.platform !== "win32" && existsSync7(binaryPath)) {
|
|
15385
15462
|
chmodSync2(binaryPath, 493);
|
|
15386
15463
|
}
|
|
15387
15464
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -15432,8 +15509,8 @@ function findSgCliPathSync() {
|
|
|
15432
15509
|
const require2 = createRequire4(import.meta.url);
|
|
15433
15510
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
15434
15511
|
const cliDir = dirname2(cliPkgPath);
|
|
15435
|
-
const sgPath =
|
|
15436
|
-
if (
|
|
15512
|
+
const sgPath = join6(cliDir, binaryName);
|
|
15513
|
+
if (existsSync8(sgPath) && isValidBinary(sgPath)) {
|
|
15437
15514
|
return sgPath;
|
|
15438
15515
|
}
|
|
15439
15516
|
} catch {}
|
|
@@ -15444,17 +15521,17 @@ function findSgCliPathSync() {
|
|
|
15444
15521
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
15445
15522
|
const pkgDir = dirname2(pkgPath);
|
|
15446
15523
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
15447
|
-
const binaryPath =
|
|
15448
|
-
if (
|
|
15524
|
+
const binaryPath = join6(pkgDir, astGrepName);
|
|
15525
|
+
if (existsSync8(binaryPath) && isValidBinary(binaryPath)) {
|
|
15449
15526
|
return binaryPath;
|
|
15450
15527
|
}
|
|
15451
15528
|
} catch {}
|
|
15452
15529
|
}
|
|
15453
15530
|
if (process.platform === "darwin") {
|
|
15454
15531
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
15455
|
-
for (const
|
|
15456
|
-
if (
|
|
15457
|
-
return
|
|
15532
|
+
for (const path2 of homebrewPaths) {
|
|
15533
|
+
if (existsSync8(path2) && isValidBinary(path2)) {
|
|
15534
|
+
return path2;
|
|
15458
15535
|
}
|
|
15459
15536
|
}
|
|
15460
15537
|
}
|
|
@@ -15472,8 +15549,8 @@ function getSgCliPath() {
|
|
|
15472
15549
|
}
|
|
15473
15550
|
return "sg";
|
|
15474
15551
|
}
|
|
15475
|
-
function setSgCliPath(
|
|
15476
|
-
resolvedCliPath2 =
|
|
15552
|
+
function setSgCliPath(path2) {
|
|
15553
|
+
resolvedCliPath2 = path2;
|
|
15477
15554
|
}
|
|
15478
15555
|
var SG_CLI_PATH = getSgCliPath();
|
|
15479
15556
|
var CLI_LANGUAGES = [
|
|
@@ -15537,11 +15614,11 @@ var LANG_EXTENSIONS = {
|
|
|
15537
15614
|
|
|
15538
15615
|
// src/tools/ast-grep/cli.ts
|
|
15539
15616
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
15540
|
-
import { existsSync as
|
|
15617
|
+
import { existsSync as existsSync9 } from "fs";
|
|
15541
15618
|
var resolvedCliPath3 = null;
|
|
15542
15619
|
var initPromise2 = null;
|
|
15543
15620
|
async function getAstGrepPath() {
|
|
15544
|
-
if (resolvedCliPath3 !== null &&
|
|
15621
|
+
if (resolvedCliPath3 !== null && existsSync9(resolvedCliPath3)) {
|
|
15545
15622
|
return resolvedCliPath3;
|
|
15546
15623
|
}
|
|
15547
15624
|
if (initPromise2) {
|
|
@@ -15549,7 +15626,7 @@ async function getAstGrepPath() {
|
|
|
15549
15626
|
}
|
|
15550
15627
|
initPromise2 = (async () => {
|
|
15551
15628
|
const syncPath = findSgCliPathSync();
|
|
15552
|
-
if (syncPath &&
|
|
15629
|
+
if (syncPath && existsSync9(syncPath)) {
|
|
15553
15630
|
resolvedCliPath3 = syncPath;
|
|
15554
15631
|
setSgCliPath(syncPath);
|
|
15555
15632
|
return syncPath;
|
|
@@ -15583,7 +15660,7 @@ async function runSg(options) {
|
|
|
15583
15660
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
15584
15661
|
args.push(...paths);
|
|
15585
15662
|
let cliPath = getSgCliPath();
|
|
15586
|
-
if (!
|
|
15663
|
+
if (!existsSync9(cliPath) && cliPath !== "sg") {
|
|
15587
15664
|
const downloadedPath = await getAstGrepPath();
|
|
15588
15665
|
if (downloadedPath) {
|
|
15589
15666
|
cliPath = downloadedPath;
|
|
@@ -16028,8 +16105,8 @@ var ast_grep_transform = tool({
|
|
|
16028
16105
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
16029
16106
|
|
|
16030
16107
|
// src/tools/safe-grep/constants.ts
|
|
16031
|
-
import { existsSync as
|
|
16032
|
-
import { join as
|
|
16108
|
+
import { existsSync as existsSync10 } from "fs";
|
|
16109
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
16033
16110
|
import { spawnSync } from "child_process";
|
|
16034
16111
|
var cachedCli = null;
|
|
16035
16112
|
function findExecutable(name) {
|
|
@@ -16050,13 +16127,13 @@ function getOpenCodeBundledRg() {
|
|
|
16050
16127
|
const isWindows = process.platform === "win32";
|
|
16051
16128
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
16052
16129
|
const candidates = [
|
|
16053
|
-
|
|
16054
|
-
|
|
16055
|
-
|
|
16056
|
-
|
|
16130
|
+
join7(execDir, rgName),
|
|
16131
|
+
join7(execDir, "bin", rgName),
|
|
16132
|
+
join7(execDir, "..", "bin", rgName),
|
|
16133
|
+
join7(execDir, "..", "libexec", rgName)
|
|
16057
16134
|
];
|
|
16058
16135
|
for (const candidate of candidates) {
|
|
16059
|
-
if (
|
|
16136
|
+
if (existsSync10(candidate)) {
|
|
16060
16137
|
return candidate;
|
|
16061
16138
|
}
|
|
16062
16139
|
}
|
|
@@ -16389,11 +16466,11 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
16389
16466
|
});
|
|
16390
16467
|
// src/index.ts
|
|
16391
16468
|
import * as fs3 from "fs";
|
|
16392
|
-
import * as
|
|
16469
|
+
import * as path2 from "path";
|
|
16393
16470
|
function loadPluginConfig(directory) {
|
|
16394
16471
|
const configPaths = [
|
|
16395
|
-
|
|
16396
|
-
|
|
16472
|
+
path2.join(directory, "oh-my-opencode.json"),
|
|
16473
|
+
path2.join(directory, ".oh-my-opencode.json")
|
|
16397
16474
|
];
|
|
16398
16475
|
for (const configPath of configPaths) {
|
|
16399
16476
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LSPClient } from "./client";
|
|
2
|
-
import type { HoverResult, DocumentSymbol, SymbolInfo, Location, LocationLink, Diagnostic, PrepareRenameResult, PrepareRenameDefaultBehavior, WorkspaceEdit, TextEdit, CodeAction, Command } from "./types";
|
|
2
|
+
import type { HoverResult, DocumentSymbol, SymbolInfo, Location, LocationLink, Diagnostic, PrepareRenameResult, PrepareRenameDefaultBehavior, Range, WorkspaceEdit, TextEdit, CodeAction, Command } from "./types";
|
|
3
3
|
export declare function findWorkspaceRoot(filePath: string): string;
|
|
4
4
|
export declare function withLspClient<T>(filePath: string, fn: (client: LSPClient) => Promise<T>): Promise<T>;
|
|
5
5
|
export declare function formatHoverResult(result: HoverResult | null): string;
|
|
@@ -10,7 +10,7 @@ export declare function formatDocumentSymbol(symbol: DocumentSymbol, indent?: nu
|
|
|
10
10
|
export declare function formatSymbolInfo(symbol: SymbolInfo): string;
|
|
11
11
|
export declare function formatDiagnostic(diag: Diagnostic): string;
|
|
12
12
|
export declare function filterDiagnosticsBySeverity(diagnostics: Diagnostic[], severityFilter?: "error" | "warning" | "information" | "hint" | "all"): Diagnostic[];
|
|
13
|
-
export declare function formatPrepareRenameResult(result: PrepareRenameResult | PrepareRenameDefaultBehavior | null): string;
|
|
13
|
+
export declare function formatPrepareRenameResult(result: PrepareRenameResult | PrepareRenameDefaultBehavior | Range | null): string;
|
|
14
14
|
export declare function formatTextEdit(edit: TextEdit): string;
|
|
15
15
|
export declare function formatWorkspaceEdit(edit: WorkspaceEdit | null): string;
|
|
16
16
|
export declare function formatCodeAction(action: CodeAction): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"@ast-grep/napi": "^0.40.0",
|
|
47
47
|
"@code-yeongyu/comment-checker": "^0.4.1",
|
|
48
48
|
"@opencode-ai/plugin": "^1.0.7",
|
|
49
|
+
"xdg-basedir": "^5.1.0",
|
|
49
50
|
"zod": "^4.1.8"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|