oh-my-opencode 0.1.23 → 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 +414 -230
- package/dist/tools/ast-grep/cli.d.ts +2 -2
- package/dist/tools/ast-grep/constants.d.ts +3 -0
- package/dist/tools/ast-grep/types.d.ts +7 -0
- package/dist/tools/ast-grep/utils.d.ts +3 -3
- package/dist/tools/lsp/constants.d.ts +3 -0
- 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
|
}
|
|
@@ -1571,6 +1638,9 @@ var SEVERITY_MAP = {
|
|
|
1571
1638
|
3: "information",
|
|
1572
1639
|
4: "hint"
|
|
1573
1640
|
};
|
|
1641
|
+
var DEFAULT_MAX_REFERENCES = 200;
|
|
1642
|
+
var DEFAULT_MAX_SYMBOLS = 200;
|
|
1643
|
+
var DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
1574
1644
|
var BUILTIN_SERVERS = {
|
|
1575
1645
|
typescript: {
|
|
1576
1646
|
command: ["typescript-language-server", "--stdio"],
|
|
@@ -1707,14 +1777,14 @@ var EXT_TO_LANG = {
|
|
|
1707
1777
|
".yml": "yaml"
|
|
1708
1778
|
};
|
|
1709
1779
|
// src/tools/lsp/config.ts
|
|
1710
|
-
import { existsSync as
|
|
1711
|
-
import { join as
|
|
1780
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
1781
|
+
import { join as join4 } from "path";
|
|
1712
1782
|
import { homedir as homedir2 } from "os";
|
|
1713
|
-
function loadJsonFile(
|
|
1714
|
-
if (!
|
|
1783
|
+
function loadJsonFile(path2) {
|
|
1784
|
+
if (!existsSync5(path2))
|
|
1715
1785
|
return null;
|
|
1716
1786
|
try {
|
|
1717
|
-
return JSON.parse(
|
|
1787
|
+
return JSON.parse(readFileSync2(path2, "utf-8"));
|
|
1718
1788
|
} catch {
|
|
1719
1789
|
return null;
|
|
1720
1790
|
}
|
|
@@ -1722,9 +1792,9 @@ function loadJsonFile(path) {
|
|
|
1722
1792
|
function getConfigPaths() {
|
|
1723
1793
|
const cwd = process.cwd();
|
|
1724
1794
|
return {
|
|
1725
|
-
project:
|
|
1726
|
-
user:
|
|
1727
|
-
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")
|
|
1728
1798
|
};
|
|
1729
1799
|
}
|
|
1730
1800
|
function loadAllConfigs() {
|
|
@@ -1817,7 +1887,7 @@ function isServerInstalled(command) {
|
|
|
1817
1887
|
const pathEnv = process.env.PATH || "";
|
|
1818
1888
|
const paths = pathEnv.split(":");
|
|
1819
1889
|
for (const p of paths) {
|
|
1820
|
-
if (
|
|
1890
|
+
if (existsSync5(join4(p, cmd))) {
|
|
1821
1891
|
return true;
|
|
1822
1892
|
}
|
|
1823
1893
|
}
|
|
@@ -1867,7 +1937,7 @@ function getAllServers() {
|
|
|
1867
1937
|
}
|
|
1868
1938
|
// src/tools/lsp/client.ts
|
|
1869
1939
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
1870
|
-
import { readFileSync as
|
|
1940
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1871
1941
|
import { extname, resolve } from "path";
|
|
1872
1942
|
class LSPServerManager {
|
|
1873
1943
|
static instance;
|
|
@@ -2254,7 +2324,7 @@ ${msg}`);
|
|
|
2254
2324
|
const absPath = resolve(filePath);
|
|
2255
2325
|
if (this.openedFiles.has(absPath))
|
|
2256
2326
|
return;
|
|
2257
|
-
const text =
|
|
2327
|
+
const text = readFileSync3(absPath, "utf-8");
|
|
2258
2328
|
const ext = extname(absPath);
|
|
2259
2329
|
const languageId = getLanguageId(ext);
|
|
2260
2330
|
this.notify("textDocument/didOpen", {
|
|
@@ -2361,16 +2431,16 @@ ${msg}`);
|
|
|
2361
2431
|
}
|
|
2362
2432
|
// src/tools/lsp/utils.ts
|
|
2363
2433
|
import { extname as extname2, resolve as resolve2 } from "path";
|
|
2364
|
-
import { existsSync as
|
|
2434
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
2365
2435
|
function findWorkspaceRoot(filePath) {
|
|
2366
2436
|
let dir = resolve2(filePath);
|
|
2367
|
-
if (!
|
|
2437
|
+
if (!existsSync6(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
2368
2438
|
dir = __require("path").dirname(dir);
|
|
2369
2439
|
}
|
|
2370
2440
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
2371
2441
|
while (dir !== "/") {
|
|
2372
2442
|
for (const marker of markers) {
|
|
2373
|
-
if (
|
|
2443
|
+
if (existsSync6(__require("path").join(dir, marker))) {
|
|
2374
2444
|
return dir;
|
|
2375
2445
|
}
|
|
2376
2446
|
}
|
|
@@ -2484,12 +2554,22 @@ function formatPrepareRenameResult(result) {
|
|
|
2484
2554
|
if ("defaultBehavior" in result) {
|
|
2485
2555
|
return result.defaultBehavior ? "Rename supported (using default behavior)" : "Cannot rename at this position";
|
|
2486
2556
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
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";
|
|
2493
2573
|
}
|
|
2494
2574
|
function formatCodeAction(action) {
|
|
2495
2575
|
let result = `[${action.kind || "action"}] ${action.title}`;
|
|
@@ -2518,7 +2598,7 @@ function formatCodeActions(actions) {
|
|
|
2518
2598
|
}
|
|
2519
2599
|
function applyTextEditsToFile(filePath, edits) {
|
|
2520
2600
|
try {
|
|
2521
|
-
let content =
|
|
2601
|
+
let content = readFileSync4(filePath, "utf-8");
|
|
2522
2602
|
const lines = content.split(`
|
|
2523
2603
|
`);
|
|
2524
2604
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -2543,7 +2623,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
2543
2623
|
`));
|
|
2544
2624
|
}
|
|
2545
2625
|
}
|
|
2546
|
-
|
|
2626
|
+
writeFileSync2(filePath, lines.join(`
|
|
2547
2627
|
`), "utf-8");
|
|
2548
2628
|
return { success: true, editCount: edits.length };
|
|
2549
2629
|
} catch (err) {
|
|
@@ -2574,7 +2654,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
2574
2654
|
if (change.kind === "create") {
|
|
2575
2655
|
try {
|
|
2576
2656
|
const filePath = change.uri.replace("file://", "");
|
|
2577
|
-
|
|
2657
|
+
writeFileSync2(filePath, "", "utf-8");
|
|
2578
2658
|
result.filesModified.push(filePath);
|
|
2579
2659
|
} catch (err) {
|
|
2580
2660
|
result.success = false;
|
|
@@ -2584,8 +2664,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
2584
2664
|
try {
|
|
2585
2665
|
const oldPath = change.oldUri.replace("file://", "");
|
|
2586
2666
|
const newPath = change.newUri.replace("file://", "");
|
|
2587
|
-
const content =
|
|
2588
|
-
|
|
2667
|
+
const content = readFileSync4(oldPath, "utf-8");
|
|
2668
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
2589
2669
|
__require("fs").unlinkSync(oldPath);
|
|
2590
2670
|
result.filesModified.push(newPath);
|
|
2591
2671
|
} catch (err) {
|
|
@@ -3365,10 +3445,10 @@ function mergeDefs(...defs) {
|
|
|
3365
3445
|
function cloneDef(schema) {
|
|
3366
3446
|
return mergeDefs(schema._zod.def);
|
|
3367
3447
|
}
|
|
3368
|
-
function getElementAtPath(obj,
|
|
3369
|
-
if (!
|
|
3448
|
+
function getElementAtPath(obj, path2) {
|
|
3449
|
+
if (!path2)
|
|
3370
3450
|
return obj;
|
|
3371
|
-
return
|
|
3451
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
3372
3452
|
}
|
|
3373
3453
|
function promiseAllObject(promisesObj) {
|
|
3374
3454
|
const keys = Object.keys(promisesObj);
|
|
@@ -3727,11 +3807,11 @@ function aborted(x, startIndex = 0) {
|
|
|
3727
3807
|
}
|
|
3728
3808
|
return false;
|
|
3729
3809
|
}
|
|
3730
|
-
function prefixIssues(
|
|
3810
|
+
function prefixIssues(path2, issues) {
|
|
3731
3811
|
return issues.map((iss) => {
|
|
3732
3812
|
var _a;
|
|
3733
3813
|
(_a = iss).path ?? (_a.path = []);
|
|
3734
|
-
iss.path.unshift(
|
|
3814
|
+
iss.path.unshift(path2);
|
|
3735
3815
|
return iss;
|
|
3736
3816
|
});
|
|
3737
3817
|
}
|
|
@@ -3899,7 +3979,7 @@ function treeifyError(error, _mapper) {
|
|
|
3899
3979
|
return issue2.message;
|
|
3900
3980
|
};
|
|
3901
3981
|
const result = { errors: [] };
|
|
3902
|
-
const processError = (error2,
|
|
3982
|
+
const processError = (error2, path2 = []) => {
|
|
3903
3983
|
var _a, _b;
|
|
3904
3984
|
for (const issue2 of error2.issues) {
|
|
3905
3985
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -3909,7 +3989,7 @@ function treeifyError(error, _mapper) {
|
|
|
3909
3989
|
} else if (issue2.code === "invalid_element") {
|
|
3910
3990
|
processError({ issues: issue2.issues }, issue2.path);
|
|
3911
3991
|
} else {
|
|
3912
|
-
const fullpath = [...
|
|
3992
|
+
const fullpath = [...path2, ...issue2.path];
|
|
3913
3993
|
if (fullpath.length === 0) {
|
|
3914
3994
|
result.errors.push(mapper(issue2));
|
|
3915
3995
|
continue;
|
|
@@ -3941,8 +4021,8 @@ function treeifyError(error, _mapper) {
|
|
|
3941
4021
|
}
|
|
3942
4022
|
function toDotPath(_path) {
|
|
3943
4023
|
const segs = [];
|
|
3944
|
-
const
|
|
3945
|
-
for (const seg of
|
|
4024
|
+
const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4025
|
+
for (const seg of path2) {
|
|
3946
4026
|
if (typeof seg === "number")
|
|
3947
4027
|
segs.push(`[${seg}]`);
|
|
3948
4028
|
else if (typeof seg === "symbol")
|
|
@@ -15025,7 +15105,14 @@ var lsp_find_references = tool({
|
|
|
15025
15105
|
const output2 = "No references found";
|
|
15026
15106
|
return output2;
|
|
15027
15107
|
}
|
|
15028
|
-
const
|
|
15108
|
+
const total = result.length;
|
|
15109
|
+
const truncated = total > DEFAULT_MAX_REFERENCES;
|
|
15110
|
+
const limited = truncated ? result.slice(0, DEFAULT_MAX_REFERENCES) : result;
|
|
15111
|
+
const lines = limited.map(formatLocation);
|
|
15112
|
+
if (truncated) {
|
|
15113
|
+
lines.unshift(`Found ${total} references (showing first ${DEFAULT_MAX_REFERENCES}):`);
|
|
15114
|
+
}
|
|
15115
|
+
const output = lines.join(`
|
|
15029
15116
|
`);
|
|
15030
15117
|
return output;
|
|
15031
15118
|
} catch (e) {
|
|
@@ -15045,18 +15132,23 @@ var lsp_document_symbols = tool({
|
|
|
15045
15132
|
return await client.documentSymbols(args.filePath);
|
|
15046
15133
|
});
|
|
15047
15134
|
if (!result || result.length === 0) {
|
|
15048
|
-
const
|
|
15049
|
-
return
|
|
15135
|
+
const output = "No symbols found";
|
|
15136
|
+
return output;
|
|
15050
15137
|
}
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
|
|
15138
|
+
const total = result.length;
|
|
15139
|
+
const truncated = total > DEFAULT_MAX_SYMBOLS;
|
|
15140
|
+
const limited = truncated ? result.slice(0, DEFAULT_MAX_SYMBOLS) : result;
|
|
15141
|
+
const lines = [];
|
|
15142
|
+
if (truncated) {
|
|
15143
|
+
lines.push(`Found ${total} symbols (showing first ${DEFAULT_MAX_SYMBOLS}):`);
|
|
15144
|
+
}
|
|
15145
|
+
if ("range" in limited[0]) {
|
|
15146
|
+
lines.push(...limited.map((s) => formatDocumentSymbol(s)));
|
|
15055
15147
|
} else {
|
|
15056
|
-
|
|
15057
|
-
`);
|
|
15148
|
+
lines.push(...limited.map(formatSymbolInfo));
|
|
15058
15149
|
}
|
|
15059
|
-
return
|
|
15150
|
+
return lines.join(`
|
|
15151
|
+
`);
|
|
15060
15152
|
} catch (e) {
|
|
15061
15153
|
const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
15062
15154
|
return output;
|
|
@@ -15079,8 +15171,15 @@ var lsp_workspace_symbols = tool({
|
|
|
15079
15171
|
const output2 = "No symbols found";
|
|
15080
15172
|
return output2;
|
|
15081
15173
|
}
|
|
15082
|
-
const
|
|
15083
|
-
const
|
|
15174
|
+
const total = result.length;
|
|
15175
|
+
const limit = Math.min(args.limit ?? DEFAULT_MAX_SYMBOLS, DEFAULT_MAX_SYMBOLS);
|
|
15176
|
+
const truncated = total > limit;
|
|
15177
|
+
const limited = result.slice(0, limit);
|
|
15178
|
+
const lines = limited.map(formatSymbolInfo);
|
|
15179
|
+
if (truncated) {
|
|
15180
|
+
lines.unshift(`Found ${total} symbols (showing first ${limit}):`);
|
|
15181
|
+
}
|
|
15182
|
+
const output = lines.join(`
|
|
15084
15183
|
`);
|
|
15085
15184
|
return output;
|
|
15086
15185
|
} catch (e) {
|
|
@@ -15113,7 +15212,14 @@ var lsp_diagnostics = tool({
|
|
|
15113
15212
|
const output2 = "No diagnostics found";
|
|
15114
15213
|
return output2;
|
|
15115
15214
|
}
|
|
15116
|
-
const
|
|
15215
|
+
const total = diagnostics.length;
|
|
15216
|
+
const truncated = total > DEFAULT_MAX_DIAGNOSTICS;
|
|
15217
|
+
const limited = truncated ? diagnostics.slice(0, DEFAULT_MAX_DIAGNOSTICS) : diagnostics;
|
|
15218
|
+
const lines = limited.map(formatDiagnostic);
|
|
15219
|
+
if (truncated) {
|
|
15220
|
+
lines.unshift(`Found ${total} diagnostics (showing first ${DEFAULT_MAX_DIAGNOSTICS}):`);
|
|
15221
|
+
}
|
|
15222
|
+
const output = lines.join(`
|
|
15117
15223
|
`);
|
|
15118
15224
|
return output;
|
|
15119
15225
|
} catch (e) {
|
|
@@ -15259,13 +15365,13 @@ var lsp_code_action_resolve = tool({
|
|
|
15259
15365
|
});
|
|
15260
15366
|
// src/tools/ast-grep/constants.ts
|
|
15261
15367
|
import { createRequire as createRequire4 } from "module";
|
|
15262
|
-
import { dirname as dirname2, join as
|
|
15263
|
-
import { existsSync as
|
|
15368
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
15369
|
+
import { existsSync as existsSync8, statSync } from "fs";
|
|
15264
15370
|
|
|
15265
15371
|
// src/tools/ast-grep/downloader.ts
|
|
15266
15372
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
15267
|
-
import { existsSync as
|
|
15268
|
-
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";
|
|
15269
15375
|
import { homedir as homedir3 } from "os";
|
|
15270
15376
|
import { createRequire as createRequire3 } from "module";
|
|
15271
15377
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -15291,19 +15397,19 @@ var PLATFORM_MAP2 = {
|
|
|
15291
15397
|
function getCacheDir2() {
|
|
15292
15398
|
if (process.platform === "win32") {
|
|
15293
15399
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
15294
|
-
const base2 = localAppData ||
|
|
15295
|
-
return
|
|
15400
|
+
const base2 = localAppData || join5(homedir3(), "AppData", "Local");
|
|
15401
|
+
return join5(base2, "oh-my-opencode", "bin");
|
|
15296
15402
|
}
|
|
15297
|
-
const
|
|
15298
|
-
const base =
|
|
15299
|
-
return
|
|
15403
|
+
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
15404
|
+
const base = xdgCache2 || join5(homedir3(), ".cache");
|
|
15405
|
+
return join5(base, "oh-my-opencode", "bin");
|
|
15300
15406
|
}
|
|
15301
15407
|
function getBinaryName3() {
|
|
15302
15408
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
15303
15409
|
}
|
|
15304
15410
|
function getCachedBinaryPath2() {
|
|
15305
|
-
const binaryPath =
|
|
15306
|
-
return
|
|
15411
|
+
const binaryPath = join5(getCacheDir2(), getBinaryName3());
|
|
15412
|
+
return existsSync7(binaryPath) ? binaryPath : null;
|
|
15307
15413
|
}
|
|
15308
15414
|
async function extractZip2(archivePath, destDir) {
|
|
15309
15415
|
const proc = process.platform === "win32" ? spawn4([
|
|
@@ -15329,30 +15435,30 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
15329
15435
|
}
|
|
15330
15436
|
const cacheDir = getCacheDir2();
|
|
15331
15437
|
const binaryName = getBinaryName3();
|
|
15332
|
-
const binaryPath =
|
|
15333
|
-
if (
|
|
15438
|
+
const binaryPath = join5(cacheDir, binaryName);
|
|
15439
|
+
if (existsSync7(binaryPath)) {
|
|
15334
15440
|
return binaryPath;
|
|
15335
15441
|
}
|
|
15336
|
-
const { arch, os } = platformInfo;
|
|
15337
|
-
const assetName = `app-${arch}-${
|
|
15442
|
+
const { arch, os: os2 } = platformInfo;
|
|
15443
|
+
const assetName = `app-${arch}-${os2}.zip`;
|
|
15338
15444
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
15339
15445
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
15340
15446
|
try {
|
|
15341
|
-
if (!
|
|
15342
|
-
|
|
15447
|
+
if (!existsSync7(cacheDir)) {
|
|
15448
|
+
mkdirSync3(cacheDir, { recursive: true });
|
|
15343
15449
|
}
|
|
15344
15450
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
15345
15451
|
if (!response.ok) {
|
|
15346
15452
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
15347
15453
|
}
|
|
15348
|
-
const archivePath =
|
|
15454
|
+
const archivePath = join5(cacheDir, assetName);
|
|
15349
15455
|
const arrayBuffer = await response.arrayBuffer();
|
|
15350
15456
|
await Bun.write(archivePath, arrayBuffer);
|
|
15351
15457
|
await extractZip2(archivePath, cacheDir);
|
|
15352
|
-
if (
|
|
15458
|
+
if (existsSync7(archivePath)) {
|
|
15353
15459
|
unlinkSync2(archivePath);
|
|
15354
15460
|
}
|
|
15355
|
-
if (process.platform !== "win32" &&
|
|
15461
|
+
if (process.platform !== "win32" && existsSync7(binaryPath)) {
|
|
15356
15462
|
chmodSync2(binaryPath, 493);
|
|
15357
15463
|
}
|
|
15358
15464
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -15403,8 +15509,8 @@ function findSgCliPathSync() {
|
|
|
15403
15509
|
const require2 = createRequire4(import.meta.url);
|
|
15404
15510
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
15405
15511
|
const cliDir = dirname2(cliPkgPath);
|
|
15406
|
-
const sgPath =
|
|
15407
|
-
if (
|
|
15512
|
+
const sgPath = join6(cliDir, binaryName);
|
|
15513
|
+
if (existsSync8(sgPath) && isValidBinary(sgPath)) {
|
|
15408
15514
|
return sgPath;
|
|
15409
15515
|
}
|
|
15410
15516
|
} catch {}
|
|
@@ -15415,17 +15521,17 @@ function findSgCliPathSync() {
|
|
|
15415
15521
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
15416
15522
|
const pkgDir = dirname2(pkgPath);
|
|
15417
15523
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
15418
|
-
const binaryPath =
|
|
15419
|
-
if (
|
|
15524
|
+
const binaryPath = join6(pkgDir, astGrepName);
|
|
15525
|
+
if (existsSync8(binaryPath) && isValidBinary(binaryPath)) {
|
|
15420
15526
|
return binaryPath;
|
|
15421
15527
|
}
|
|
15422
15528
|
} catch {}
|
|
15423
15529
|
}
|
|
15424
15530
|
if (process.platform === "darwin") {
|
|
15425
15531
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
15426
|
-
for (const
|
|
15427
|
-
if (
|
|
15428
|
-
return
|
|
15532
|
+
for (const path2 of homebrewPaths) {
|
|
15533
|
+
if (existsSync8(path2) && isValidBinary(path2)) {
|
|
15534
|
+
return path2;
|
|
15429
15535
|
}
|
|
15430
15536
|
}
|
|
15431
15537
|
}
|
|
@@ -15443,8 +15549,8 @@ function getSgCliPath() {
|
|
|
15443
15549
|
}
|
|
15444
15550
|
return "sg";
|
|
15445
15551
|
}
|
|
15446
|
-
function setSgCliPath(
|
|
15447
|
-
resolvedCliPath2 =
|
|
15552
|
+
function setSgCliPath(path2) {
|
|
15553
|
+
resolvedCliPath2 = path2;
|
|
15448
15554
|
}
|
|
15449
15555
|
var SG_CLI_PATH = getSgCliPath();
|
|
15450
15556
|
var CLI_LANGUAGES = [
|
|
@@ -15475,6 +15581,9 @@ var CLI_LANGUAGES = [
|
|
|
15475
15581
|
"yaml"
|
|
15476
15582
|
];
|
|
15477
15583
|
var NAPI_LANGUAGES = ["html", "javascript", "tsx", "css", "typescript"];
|
|
15584
|
+
var DEFAULT_TIMEOUT_MS = 300000;
|
|
15585
|
+
var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
15586
|
+
var DEFAULT_MAX_MATCHES = 500;
|
|
15478
15587
|
var LANG_EXTENSIONS = {
|
|
15479
15588
|
bash: [".bash", ".sh", ".zsh", ".bats"],
|
|
15480
15589
|
c: [".c", ".h"],
|
|
@@ -15505,11 +15614,11 @@ var LANG_EXTENSIONS = {
|
|
|
15505
15614
|
|
|
15506
15615
|
// src/tools/ast-grep/cli.ts
|
|
15507
15616
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
15508
|
-
import { existsSync as
|
|
15617
|
+
import { existsSync as existsSync9 } from "fs";
|
|
15509
15618
|
var resolvedCliPath3 = null;
|
|
15510
15619
|
var initPromise2 = null;
|
|
15511
15620
|
async function getAstGrepPath() {
|
|
15512
|
-
if (resolvedCliPath3 !== null &&
|
|
15621
|
+
if (resolvedCliPath3 !== null && existsSync9(resolvedCliPath3)) {
|
|
15513
15622
|
return resolvedCliPath3;
|
|
15514
15623
|
}
|
|
15515
15624
|
if (initPromise2) {
|
|
@@ -15517,7 +15626,7 @@ async function getAstGrepPath() {
|
|
|
15517
15626
|
}
|
|
15518
15627
|
initPromise2 = (async () => {
|
|
15519
15628
|
const syncPath = findSgCliPathSync();
|
|
15520
|
-
if (syncPath &&
|
|
15629
|
+
if (syncPath && existsSync9(syncPath)) {
|
|
15521
15630
|
resolvedCliPath3 = syncPath;
|
|
15522
15631
|
setSgCliPath(syncPath);
|
|
15523
15632
|
return syncPath;
|
|
@@ -15532,16 +15641,6 @@ async function getAstGrepPath() {
|
|
|
15532
15641
|
})();
|
|
15533
15642
|
return initPromise2;
|
|
15534
15643
|
}
|
|
15535
|
-
async function spawnSg(cliPath, args) {
|
|
15536
|
-
const proc = spawn5([cliPath, ...args], {
|
|
15537
|
-
stdout: "pipe",
|
|
15538
|
-
stderr: "pipe"
|
|
15539
|
-
});
|
|
15540
|
-
const stdout = await new Response(proc.stdout).text();
|
|
15541
|
-
const stderr = await new Response(proc.stderr).text();
|
|
15542
|
-
const exitCode = await proc.exited;
|
|
15543
|
-
return { stdout, stderr, exitCode };
|
|
15544
|
-
}
|
|
15545
15644
|
async function runSg(options) {
|
|
15546
15645
|
const args = ["run", "-p", options.pattern, "--lang", options.lang, "--json=compact"];
|
|
15547
15646
|
if (options.rewrite) {
|
|
@@ -15561,53 +15660,120 @@ async function runSg(options) {
|
|
|
15561
15660
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
15562
15661
|
args.push(...paths);
|
|
15563
15662
|
let cliPath = getSgCliPath();
|
|
15564
|
-
if (!
|
|
15663
|
+
if (!existsSync9(cliPath) && cliPath !== "sg") {
|
|
15565
15664
|
const downloadedPath = await getAstGrepPath();
|
|
15566
15665
|
if (downloadedPath) {
|
|
15567
15666
|
cliPath = downloadedPath;
|
|
15568
15667
|
}
|
|
15569
15668
|
}
|
|
15570
|
-
|
|
15669
|
+
const timeout = DEFAULT_TIMEOUT_MS;
|
|
15670
|
+
const proc = spawn5([cliPath, ...args], {
|
|
15671
|
+
stdout: "pipe",
|
|
15672
|
+
stderr: "pipe"
|
|
15673
|
+
});
|
|
15674
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
15675
|
+
const id = setTimeout(() => {
|
|
15676
|
+
proc.kill();
|
|
15677
|
+
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
15678
|
+
}, timeout);
|
|
15679
|
+
proc.exited.then(() => clearTimeout(id));
|
|
15680
|
+
});
|
|
15681
|
+
let stdout;
|
|
15682
|
+
let stderr;
|
|
15683
|
+
let exitCode;
|
|
15571
15684
|
try {
|
|
15572
|
-
|
|
15685
|
+
stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise]);
|
|
15686
|
+
stderr = await new Response(proc.stderr).text();
|
|
15687
|
+
exitCode = await proc.exited;
|
|
15573
15688
|
} catch (e) {
|
|
15574
15689
|
const error45 = e;
|
|
15575
|
-
if (error45.
|
|
15690
|
+
if (error45.message?.includes("timeout")) {
|
|
15691
|
+
return {
|
|
15692
|
+
matches: [],
|
|
15693
|
+
totalMatches: 0,
|
|
15694
|
+
truncated: true,
|
|
15695
|
+
truncatedReason: "timeout",
|
|
15696
|
+
error: error45.message
|
|
15697
|
+
};
|
|
15698
|
+
}
|
|
15699
|
+
const nodeError = e;
|
|
15700
|
+
if (nodeError.code === "ENOENT" || nodeError.message?.includes("ENOENT") || nodeError.message?.includes("not found")) {
|
|
15576
15701
|
const downloadedPath = await ensureAstGrepBinary();
|
|
15577
15702
|
if (downloadedPath) {
|
|
15578
15703
|
resolvedCliPath3 = downloadedPath;
|
|
15579
15704
|
setSgCliPath(downloadedPath);
|
|
15580
|
-
|
|
15705
|
+
return runSg(options);
|
|
15581
15706
|
} else {
|
|
15582
|
-
|
|
15707
|
+
return {
|
|
15708
|
+
matches: [],
|
|
15709
|
+
totalMatches: 0,
|
|
15710
|
+
truncated: false,
|
|
15711
|
+
error: `ast-grep CLI binary not found.
|
|
15583
15712
|
|
|
15584
15713
|
` + `Auto-download failed. Manual install options:
|
|
15585
15714
|
` + ` bun add -D @ast-grep/cli
|
|
15586
15715
|
` + ` cargo install ast-grep --locked
|
|
15587
|
-
` + ` brew install ast-grep`
|
|
15716
|
+
` + ` brew install ast-grep`
|
|
15717
|
+
};
|
|
15588
15718
|
}
|
|
15589
|
-
} else {
|
|
15590
|
-
throw new Error(`Failed to spawn ast-grep: ${error45.message}`);
|
|
15591
15719
|
}
|
|
15720
|
+
return {
|
|
15721
|
+
matches: [],
|
|
15722
|
+
totalMatches: 0,
|
|
15723
|
+
truncated: false,
|
|
15724
|
+
error: `Failed to spawn ast-grep: ${error45.message}`
|
|
15725
|
+
};
|
|
15592
15726
|
}
|
|
15593
|
-
const { stdout, stderr, exitCode } = result;
|
|
15594
15727
|
if (exitCode !== 0 && stdout.trim() === "") {
|
|
15595
15728
|
if (stderr.includes("No files found")) {
|
|
15596
|
-
return [];
|
|
15729
|
+
return { matches: [], totalMatches: 0, truncated: false };
|
|
15597
15730
|
}
|
|
15598
15731
|
if (stderr.trim()) {
|
|
15599
|
-
|
|
15732
|
+
return { matches: [], totalMatches: 0, truncated: false, error: stderr.trim() };
|
|
15600
15733
|
}
|
|
15601
|
-
return [];
|
|
15734
|
+
return { matches: [], totalMatches: 0, truncated: false };
|
|
15602
15735
|
}
|
|
15603
15736
|
if (!stdout.trim()) {
|
|
15604
|
-
return [];
|
|
15737
|
+
return { matches: [], totalMatches: 0, truncated: false };
|
|
15605
15738
|
}
|
|
15739
|
+
const outputTruncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES;
|
|
15740
|
+
const outputToProcess = outputTruncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES) : stdout;
|
|
15741
|
+
let matches = [];
|
|
15606
15742
|
try {
|
|
15607
|
-
|
|
15743
|
+
matches = JSON.parse(outputToProcess);
|
|
15608
15744
|
} catch {
|
|
15609
|
-
|
|
15745
|
+
if (outputTruncated) {
|
|
15746
|
+
try {
|
|
15747
|
+
const lastValidIndex = outputToProcess.lastIndexOf("}");
|
|
15748
|
+
if (lastValidIndex > 0) {
|
|
15749
|
+
const bracketIndex = outputToProcess.lastIndexOf("},", lastValidIndex);
|
|
15750
|
+
if (bracketIndex > 0) {
|
|
15751
|
+
const truncatedJson = outputToProcess.substring(0, bracketIndex + 1) + "]";
|
|
15752
|
+
matches = JSON.parse(truncatedJson);
|
|
15753
|
+
}
|
|
15754
|
+
}
|
|
15755
|
+
} catch {
|
|
15756
|
+
return {
|
|
15757
|
+
matches: [],
|
|
15758
|
+
totalMatches: 0,
|
|
15759
|
+
truncated: true,
|
|
15760
|
+
truncatedReason: "max_output_bytes",
|
|
15761
|
+
error: "Output too large and could not be parsed"
|
|
15762
|
+
};
|
|
15763
|
+
}
|
|
15764
|
+
} else {
|
|
15765
|
+
return { matches: [], totalMatches: 0, truncated: false };
|
|
15766
|
+
}
|
|
15610
15767
|
}
|
|
15768
|
+
const totalMatches = matches.length;
|
|
15769
|
+
const matchesTruncated = totalMatches > DEFAULT_MAX_MATCHES;
|
|
15770
|
+
const finalMatches = matchesTruncated ? matches.slice(0, DEFAULT_MAX_MATCHES) : matches;
|
|
15771
|
+
return {
|
|
15772
|
+
matches: finalMatches,
|
|
15773
|
+
totalMatches,
|
|
15774
|
+
truncated: outputTruncated || matchesTruncated,
|
|
15775
|
+
truncatedReason: outputTruncated ? "max_output_bytes" : matchesTruncated ? "max_matches" : undefined
|
|
15776
|
+
};
|
|
15611
15777
|
}
|
|
15612
15778
|
|
|
15613
15779
|
// src/tools/ast-grep/napi.ts
|
|
@@ -15696,13 +15862,22 @@ function getRootInfo(code, lang) {
|
|
|
15696
15862
|
}
|
|
15697
15863
|
|
|
15698
15864
|
// src/tools/ast-grep/utils.ts
|
|
15699
|
-
function formatSearchResult(
|
|
15700
|
-
if (
|
|
15865
|
+
function formatSearchResult(result) {
|
|
15866
|
+
if (result.error) {
|
|
15867
|
+
return `Error: ${result.error}`;
|
|
15868
|
+
}
|
|
15869
|
+
if (result.matches.length === 0) {
|
|
15701
15870
|
return "No matches found";
|
|
15702
15871
|
}
|
|
15703
|
-
const lines = [
|
|
15704
|
-
|
|
15705
|
-
|
|
15872
|
+
const lines = [];
|
|
15873
|
+
if (result.truncated) {
|
|
15874
|
+
const reason = result.truncatedReason === "max_matches" ? `showing first ${result.matches.length} of ${result.totalMatches}` : result.truncatedReason === "max_output_bytes" ? "output exceeded 1MB limit" : "search timed out";
|
|
15875
|
+
lines.push(`\u26A0\uFE0F Results truncated (${reason})
|
|
15876
|
+
`);
|
|
15877
|
+
}
|
|
15878
|
+
lines.push(`Found ${result.matches.length} match(es)${result.truncated ? ` (truncated from ${result.totalMatches})` : ""}:
|
|
15879
|
+
`);
|
|
15880
|
+
for (const match of result.matches) {
|
|
15706
15881
|
const loc = `${match.file}:${match.range.start.line + 1}:${match.range.start.column + 1}`;
|
|
15707
15882
|
lines.push(`${loc}`);
|
|
15708
15883
|
lines.push(` ${match.lines.trim()}`);
|
|
@@ -15711,14 +15886,23 @@ function formatSearchResult(matches) {
|
|
|
15711
15886
|
return lines.join(`
|
|
15712
15887
|
`);
|
|
15713
15888
|
}
|
|
15714
|
-
function formatReplaceResult(
|
|
15715
|
-
if (
|
|
15889
|
+
function formatReplaceResult(result, isDryRun) {
|
|
15890
|
+
if (result.error) {
|
|
15891
|
+
return `Error: ${result.error}`;
|
|
15892
|
+
}
|
|
15893
|
+
if (result.matches.length === 0) {
|
|
15716
15894
|
return "No matches found to replace";
|
|
15717
15895
|
}
|
|
15718
15896
|
const prefix = isDryRun ? "[DRY RUN] " : "";
|
|
15719
|
-
const lines = [
|
|
15720
|
-
|
|
15721
|
-
|
|
15897
|
+
const lines = [];
|
|
15898
|
+
if (result.truncated) {
|
|
15899
|
+
const reason = result.truncatedReason === "max_matches" ? `showing first ${result.matches.length} of ${result.totalMatches}` : result.truncatedReason === "max_output_bytes" ? "output exceeded 1MB limit" : "search timed out";
|
|
15900
|
+
lines.push(`\u26A0\uFE0F Results truncated (${reason})
|
|
15901
|
+
`);
|
|
15902
|
+
}
|
|
15903
|
+
lines.push(`${prefix}${result.matches.length} replacement(s):
|
|
15904
|
+
`);
|
|
15905
|
+
for (const match of result.matches) {
|
|
15722
15906
|
const loc = `${match.file}:${match.range.start.line + 1}:${match.range.start.column + 1}`;
|
|
15723
15907
|
lines.push(`${loc}`);
|
|
15724
15908
|
lines.push(` ${match.text}`);
|
|
@@ -15796,15 +15980,15 @@ var ast_grep_search = tool({
|
|
|
15796
15980
|
},
|
|
15797
15981
|
execute: async (args, context) => {
|
|
15798
15982
|
try {
|
|
15799
|
-
const
|
|
15983
|
+
const result = await runSg({
|
|
15800
15984
|
pattern: args.pattern,
|
|
15801
15985
|
lang: args.lang,
|
|
15802
15986
|
paths: args.paths,
|
|
15803
15987
|
globs: args.globs,
|
|
15804
15988
|
context: args.context
|
|
15805
15989
|
});
|
|
15806
|
-
let output = formatSearchResult(
|
|
15807
|
-
if (matches.length === 0) {
|
|
15990
|
+
let output = formatSearchResult(result);
|
|
15991
|
+
if (result.matches.length === 0 && !result.error) {
|
|
15808
15992
|
const hint = getEmptyResultHint(args.pattern, args.lang);
|
|
15809
15993
|
if (hint) {
|
|
15810
15994
|
output += `
|
|
@@ -15833,7 +16017,7 @@ var ast_grep_replace = tool({
|
|
|
15833
16017
|
},
|
|
15834
16018
|
execute: async (args, context) => {
|
|
15835
16019
|
try {
|
|
15836
|
-
const
|
|
16020
|
+
const result = await runSg({
|
|
15837
16021
|
pattern: args.pattern,
|
|
15838
16022
|
rewrite: args.rewrite,
|
|
15839
16023
|
lang: args.lang,
|
|
@@ -15841,7 +16025,7 @@ var ast_grep_replace = tool({
|
|
|
15841
16025
|
globs: args.globs,
|
|
15842
16026
|
updateAll: args.dryRun === false
|
|
15843
16027
|
});
|
|
15844
|
-
const output = formatReplaceResult(
|
|
16028
|
+
const output = formatReplaceResult(result, args.dryRun !== false);
|
|
15845
16029
|
showOutputToUser(context, output);
|
|
15846
16030
|
return output;
|
|
15847
16031
|
} catch (e) {
|
|
@@ -15921,8 +16105,8 @@ var ast_grep_transform = tool({
|
|
|
15921
16105
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
15922
16106
|
|
|
15923
16107
|
// src/tools/safe-grep/constants.ts
|
|
15924
|
-
import { existsSync as
|
|
15925
|
-
import { join as
|
|
16108
|
+
import { existsSync as existsSync10 } from "fs";
|
|
16109
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
15926
16110
|
import { spawnSync } from "child_process";
|
|
15927
16111
|
var cachedCli = null;
|
|
15928
16112
|
function findExecutable(name) {
|
|
@@ -15943,13 +16127,13 @@ function getOpenCodeBundledRg() {
|
|
|
15943
16127
|
const isWindows = process.platform === "win32";
|
|
15944
16128
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
15945
16129
|
const candidates = [
|
|
15946
|
-
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
|
|
16130
|
+
join7(execDir, rgName),
|
|
16131
|
+
join7(execDir, "bin", rgName),
|
|
16132
|
+
join7(execDir, "..", "bin", rgName),
|
|
16133
|
+
join7(execDir, "..", "libexec", rgName)
|
|
15950
16134
|
];
|
|
15951
16135
|
for (const candidate of candidates) {
|
|
15952
|
-
if (
|
|
16136
|
+
if (existsSync10(candidate)) {
|
|
15953
16137
|
return candidate;
|
|
15954
16138
|
}
|
|
15955
16139
|
}
|
|
@@ -15980,8 +16164,8 @@ var DEFAULT_MAX_DEPTH = 20;
|
|
|
15980
16164
|
var DEFAULT_MAX_FILESIZE = "10M";
|
|
15981
16165
|
var DEFAULT_MAX_COUNT = 500;
|
|
15982
16166
|
var DEFAULT_MAX_COLUMNS = 1000;
|
|
15983
|
-
var
|
|
15984
|
-
var
|
|
16167
|
+
var DEFAULT_TIMEOUT_MS2 = 300000;
|
|
16168
|
+
var DEFAULT_MAX_OUTPUT_BYTES2 = 10 * 1024 * 1024;
|
|
15985
16169
|
var RG_SAFETY_FLAGS = [
|
|
15986
16170
|
"--no-follow",
|
|
15987
16171
|
"--color=never",
|
|
@@ -16082,7 +16266,7 @@ function parseOutput(output) {
|
|
|
16082
16266
|
async function runRg(options) {
|
|
16083
16267
|
const cli = resolveGrepCli();
|
|
16084
16268
|
const args = buildArgs(options, cli.backend);
|
|
16085
|
-
const timeout = Math.min(options.timeout ??
|
|
16269
|
+
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS2, DEFAULT_TIMEOUT_MS2);
|
|
16086
16270
|
if (cli.backend === "rg") {
|
|
16087
16271
|
args.push("--", options.pattern);
|
|
16088
16272
|
} else {
|
|
@@ -16105,8 +16289,8 @@ async function runRg(options) {
|
|
|
16105
16289
|
const stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise]);
|
|
16106
16290
|
const stderr = await new Response(proc.stderr).text();
|
|
16107
16291
|
const exitCode = await proc.exited;
|
|
16108
|
-
const truncated = stdout.length >=
|
|
16109
|
-
const outputToProcess = truncated ? stdout.substring(0,
|
|
16292
|
+
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
|
|
16293
|
+
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
|
|
16110
16294
|
if (exitCode > 1 && stderr.trim()) {
|
|
16111
16295
|
return {
|
|
16112
16296
|
matches: [],
|
|
@@ -16282,11 +16466,11 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
16282
16466
|
});
|
|
16283
16467
|
// src/index.ts
|
|
16284
16468
|
import * as fs3 from "fs";
|
|
16285
|
-
import * as
|
|
16469
|
+
import * as path2 from "path";
|
|
16286
16470
|
function loadPluginConfig(directory) {
|
|
16287
16471
|
const configPaths = [
|
|
16288
|
-
|
|
16289
|
-
|
|
16472
|
+
path2.join(directory, "oh-my-opencode.json"),
|
|
16473
|
+
path2.join(directory, ".oh-my-opencode.json")
|
|
16290
16474
|
];
|
|
16291
16475
|
for (const configPath of configPaths) {
|
|
16292
16476
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CliLanguage, SgResult } from "./types";
|
|
2
2
|
export interface RunOptions {
|
|
3
3
|
pattern: string;
|
|
4
4
|
lang: CliLanguage;
|
|
@@ -10,6 +10,6 @@ export interface RunOptions {
|
|
|
10
10
|
}
|
|
11
11
|
export declare function getAstGrepPath(): Promise<string | null>;
|
|
12
12
|
export declare function startBackgroundInit(): void;
|
|
13
|
-
export declare function runSg(options: RunOptions): Promise<
|
|
13
|
+
export declare function runSg(options: RunOptions): Promise<SgResult>;
|
|
14
14
|
export declare function isCliAvailable(): boolean;
|
|
15
15
|
export declare function ensureCliAvailable(): Promise<boolean>;
|
|
@@ -4,6 +4,9 @@ export declare function setSgCliPath(path: string): void;
|
|
|
4
4
|
export declare const SG_CLI_PATH: string;
|
|
5
5
|
export declare const CLI_LANGUAGES: readonly ["bash", "c", "cpp", "csharp", "css", "elixir", "go", "haskell", "html", "java", "javascript", "json", "kotlin", "lua", "nix", "php", "python", "ruby", "rust", "scala", "solidity", "swift", "typescript", "tsx", "yaml"];
|
|
6
6
|
export declare const NAPI_LANGUAGES: readonly ["html", "javascript", "tsx", "css", "typescript"];
|
|
7
|
+
export declare const DEFAULT_TIMEOUT_MS = 300000;
|
|
8
|
+
export declare const DEFAULT_MAX_OUTPUT_BYTES: number;
|
|
9
|
+
export declare const DEFAULT_MAX_MATCHES = 500;
|
|
7
10
|
export declare const LANG_EXTENSIONS: Record<string, string[]>;
|
|
8
11
|
export interface EnvironmentCheckResult {
|
|
9
12
|
cli: {
|
|
@@ -49,3 +49,10 @@ export interface TransformResult {
|
|
|
49
49
|
transformed: string;
|
|
50
50
|
editCount: number;
|
|
51
51
|
}
|
|
52
|
+
export interface SgResult {
|
|
53
|
+
matches: CliMatch[];
|
|
54
|
+
totalMatches: number;
|
|
55
|
+
truncated: boolean;
|
|
56
|
+
truncatedReason?: "max_matches" | "max_output_bytes" | "timeout";
|
|
57
|
+
error?: string;
|
|
58
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function formatSearchResult(
|
|
3
|
-
export declare function formatReplaceResult(
|
|
1
|
+
import type { AnalyzeResult, SgResult } from "./types";
|
|
2
|
+
export declare function formatSearchResult(result: SgResult): string;
|
|
3
|
+
export declare function formatReplaceResult(result: SgResult, isDryRun: boolean): string;
|
|
4
4
|
export declare function formatAnalyzeResult(results: AnalyzeResult[], extractedMetaVars: boolean): string;
|
|
5
5
|
export declare function formatTransformResult(original: string, transformed: string, editCount: number): string;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { LSPServerConfig } from "./types";
|
|
2
2
|
export declare const SYMBOL_KIND_MAP: Record<number, string>;
|
|
3
3
|
export declare const SEVERITY_MAP: Record<number, string>;
|
|
4
|
+
export declare const DEFAULT_MAX_REFERENCES = 200;
|
|
5
|
+
export declare const DEFAULT_MAX_SYMBOLS = 200;
|
|
6
|
+
export declare const DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
4
7
|
export declare const BUILTIN_SERVERS: Record<string, Omit<LSPServerConfig, "id">>;
|
|
5
8
|
export declare const EXT_TO_LANG: Record<string, string>;
|
|
@@ -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": {
|