oh-my-opencode 0.1.16 → 0.1.18

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 CHANGED
@@ -5,10 +5,6 @@
5
5
  - [Oh My OpenCode](#oh-my-opencode)
6
6
  - [세 줄 요약](#세-줄-요약)
7
7
  - [설치](#설치)
8
- - [설정](#설정)
9
- - [특정 MCP 비활성화](#특정-mcp-비활성화)
10
- - [특정 Agent 비활성화](#특정-agent-비활성화)
11
- - [Agent 설정](#agent-설정)
12
8
  - [LLM Agent를 위한 안내](#llm-agent를-위한-안내)
13
9
  - [Why OpenCode & Why Oh My OpenCode](#why-opencode--why-oh-my-opencode)
14
10
  - [기능](#기능)
@@ -20,6 +16,7 @@
20
16
  - [Safe Grep](#safe-grep)
21
17
  - [내장 MCPs](#내장-mcps)
22
18
  - [기타 편의 기능](#기타-편의-기능)
19
+ - [설정](#설정)
23
20
  - [작성자의 노트](#작성자의-노트)
24
21
  - [주의](#주의)
25
22
 
@@ -61,45 +58,6 @@ OpenCode 가 낭만이 사라진것같은 오늘날의 시대에, 당신에게
61
58
  }
62
59
  ```
63
60
 
64
- ## 설정
65
-
66
- `oh-my-opencode.json` (또는 `.oh-my-opencode.json`) 파일을 프로젝트 루트에 생성해서 Oh My OpenCode를 입맛대로 설정할 수 있어.
67
-
68
- ```json
69
- {
70
- "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/dist/oh-my-opencode.schema.json"
71
- }
72
- ```
73
-
74
- 설정 파일은 스키마를 통해 자동완성을 지원합니다. 자세한 내용은 각 기능 섹션에서 설명합니다.
75
-
76
- ### 특정 MCP 비활성화
77
-
78
- 특정 MCP가 거슬린다면 끌 수 있어.
79
-
80
- ```json
81
- {
82
- "disabled_mcps": ["websearch_exa"]
83
- }
84
- ```
85
-
86
- ### 특정 Agent 비활성화
87
-
88
- 특정 에이전트가 마음에 안 들거나, 토큰을 아끼고 싶다면 비활성화해.
89
- 비활성화 가능한 목록: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, `document-writer`
90
-
91
- ```json
92
- {
93
- "disabled_agents": ["frontend-ui-ux-engineer"]
94
- }
95
- ```
96
-
97
- ### Agent 설정
98
-
99
- `agents` 옵션으로 에이전트의 모델, 프롬프트, 권한 등을 세밀하게 조정할 수 있습니다. 스키마를 통해 자동완성을 지원합니다.
100
-
101
- 전체 설정 옵션과 예시는 [OpenCode Agents 문서](https://opencode.ai/docs/agents)를 참고하세요.
102
-
103
61
  ## LLM Agent를 위한 안내
104
62
 
105
63
  <details>
@@ -188,24 +146,7 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
188
146
  - **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): 개발자로 전향한 디자이너라는 설정을 갖고 있습니다. 멋진 UI를 만듭니다. 아름답고 창의적인 UI 코드를 생성하는 데 탁월한 Gemini를 사용합니다.
189
147
  - **document-writer** (`google/gemini-3-pro-preview`): 기술 문서 전문가라는 설정을 갖고 있습니다. Gemini 는 문학가입니다. 글을 기가막히게 씁니다.
190
148
 
191
- #### 설정
192
-
193
- 에이전트들은 OpenCode와 동일한 설정 스펙을 따릅니다:
194
-
195
- - **모델 변경**: `agents.{name}.model`로 에이전트 모델 오버라이드. [OpenCode Models](https://opencode.ai/docs/models/#configure-models) 참고.
196
- - **MCP 비활성화**: `disabled_mcps`로 내장 MCP 끄기. [OpenCode MCP Servers](https://opencode.ai/docs/mcp-servers) 참고.
197
- - **에이전트 비활성화**: `disabled_agents` 또는 `agents.{name}.disable` 사용. [OpenCode Agents](https://opencode.ai/docs/agents) 참고.
198
-
199
- 권장하진 않지만(이 플러그인은 멀티 모델 오케스트레이션용), Anthropic만 사용하는 경우 예시:
200
-
201
- ```json
202
- {
203
- "agents": {
204
- "explore": { "model": "anthropic/claude-haiku-4-5" },
205
- "frontend-ui-ux-engineer": { "model": "anthropic/claude-opus-4" }
206
- }
207
- }
208
- ```
149
+ 에이전트의 모델, 프롬프트, 권한은 `oh-my-opencode.json`에서 커스텀할 수 있습니다. 자세한 내용은 [설정](#설정)을 참고하세요.
209
150
 
210
151
  ### Tools
211
152
 
@@ -240,9 +181,96 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
240
181
  - **websearch_exa**: Exa AI 웹 검색. 실시간 웹 검색과 콘텐츠 스크래핑을 수행합니다. 관련 웹사이트에서 LLM에 최적화된 컨텍스트를 반환합니다.
241
182
  - **context7**: 라이브러리 문서 조회. 정확한 코딩을 위해 최신 라이브러리 문서를 가져옵니다.
242
183
 
184
+ 필요 없다면 `oh-my-opencode.json`에서 비활성화할 수 있습니다:
185
+
186
+ ```json
187
+ {
188
+ "disabled_mcps": ["websearch_exa"]
189
+ }
190
+ ```
191
+
243
192
  ### 기타 편의 기능
244
193
  - **Terminal Title**: 세션 상태에 따라 터미널 타이틀을 자동 업데이트합니다 (유휴 ○, 처리중 ◐, 도구 ⚡, 에러 ✖). tmux를 지원합니다.
245
194
 
195
+ ## 설정
196
+
197
+ 설정 파일 위치 (우선순위 순):
198
+ 1. `.opencode/oh-my-opencode.json` (프로젝트)
199
+ 2. `~/.config/opencode/oh-my-opencode.json` (사용자)
200
+
201
+ Schema 자동 완성이 지원됩니다:
202
+
203
+ ```json
204
+ {
205
+ "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json"
206
+ }
207
+ ```
208
+
209
+ ### Agents
210
+
211
+ 내장 에이전트 설정을 오버라이드할 수 있습니다:
212
+
213
+ ```json
214
+ {
215
+ "agents": {
216
+ "explore": {
217
+ "model": "anthropic/claude-haiku-4-5",
218
+ "temperature": 0.5
219
+ },
220
+ "frontend-ui-ux-engineer": {
221
+ "disable": true
222
+ }
223
+ }
224
+ }
225
+ ```
226
+
227
+ 각 에이전트에서 지원하는 옵션: `model`, `temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `permission`.
228
+
229
+ 또는 `disabled_agents`로 비활성화할 수 있습니다:
230
+
231
+ ```json
232
+ {
233
+ "disabled_agents": ["oracle", "frontend-ui-ux-engineer"]
234
+ }
235
+ ```
236
+
237
+ 사용 가능한 에이전트: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, `document-writer`
238
+
239
+ ### MCPs
240
+
241
+ 내장된 MCP를 비활성화합니다:
242
+
243
+ ```json
244
+ {
245
+ "disabled_mcps": ["context7", "websearch_exa"]
246
+ }
247
+ ```
248
+
249
+ 더 자세한 내용은 [OpenCode MCP Servers](https://opencode.ai/docs/mcp-servers)를 참조하세요.
250
+
251
+ ### LSP
252
+
253
+ Oh My OpenCode의 LSP 도구는 오직 **리팩토링(이름 변경, 코드 액션)만을 위한 것**입니다. 분석용 LSP는 OpenCode 자체에서 처리합니다.
254
+
255
+ `lsp` 옵션을 통해 LSP 서버를 설정합니다:
256
+
257
+ ```json
258
+ {
259
+ "lsp": {
260
+ "typescript-language-server": {
261
+ "command": ["typescript-language-server", "--stdio"],
262
+ "extensions": [".ts", ".tsx"],
263
+ "priority": 10
264
+ },
265
+ "pylsp": {
266
+ "disabled": true
267
+ }
268
+ }
269
+ }
270
+ ```
271
+
272
+ 각 서버는 다음을 지원합니다: `command`, `extensions`, `priority`, `env`, `initialization`, `disabled`.
273
+
246
274
  ## 작성자의 노트
247
275
 
248
276
  Oh My OpenCode 를 설치하세요. 복잡하게 OpenCode 구성을 만들지마세요.
package/README.md CHANGED
@@ -6,10 +6,6 @@ English | [한국어](README.ko.md)
6
6
  - [TL;DR](#tldr)
7
7
  - [Installation](#installation)
8
8
  - [For LLM Agents](#for-llm-agents)
9
- - [Configuration](#configuration)
10
- - [Disable specific MCPs](#disable-specific-mcps)
11
- - [Disable specific Agents](#disable-specific-agents)
12
- - [Agent Configuration](#agent-configuration)
13
9
  - [Why OpenCode & Why Oh My OpenCode](#why-opencode--why-oh-my-opencode)
14
10
  - [Features](#features)
15
11
  - [Hooks](#hooks)
@@ -20,6 +16,7 @@ English | [한국어](README.ko.md)
20
16
  - [Safe Grep](#safe-grep)
21
17
  - [Built-in MCPs](#built-in-mcps)
22
18
  - [Other Features](#other-features)
19
+ - [Configuration](#configuration)
23
20
  - [Author's Note](#authors-note)
24
21
  - [Warnings](#warnings)
25
22
 
@@ -110,46 +107,6 @@ cat ~/.config/opencode/opencode.json # Should contain "oh-my-opencode" in plugi
110
107
 
111
108
  </details>
112
109
 
113
- ## Configuration
114
-
115
- You can configure Oh My OpenCode by creating a `oh-my-opencode.json` (or `.oh-my-opencode.json`) file in your project root.
116
-
117
- Configuration supports autocomplete via schema. Details are covered in each feature section below.
118
-
119
- ```json
120
- {
121
- "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/dist/oh-my-opencode.schema.json"
122
- }
123
- ```
124
-
125
- ### Disable specific MCPs
126
-
127
- If you want to disable specific built-in MCPs, you can use the `disabled_mcps` option.
128
-
129
- ```json
130
- {
131
- "disabled_mcps": ["context7", "websearch_exa"]
132
- }
133
- ```
134
-
135
- ### Disable specific Agents
136
-
137
- If you want to disable specific built-in agents, you can use the `disabled_agents` option.
138
-
139
- ```json
140
- {
141
- "disabled_agents": ["explore", "frontend-ui-ux-engineer"]
142
- }
143
- ```
144
-
145
- Available agents: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, `document-writer`
146
-
147
- ### Agent Configuration
148
-
149
- You can override any built-in agent's model, prompt, permissions, and more using the `agents` option. Configuration uses autocomplete via schema.
150
-
151
- For full configuration options and examples, see the [OpenCode Agents documentation](https://opencode.ai/docs/agents).
152
-
153
110
  ## Why OpenCode & Why Oh My OpenCode
154
111
 
155
112
  OpenCode is limitlessly extensible and customizable. Zero screen flicker.
@@ -185,24 +142,7 @@ I believe in the right tool for the job. For your wallet's sake, use CLIProxyAPI
185
142
  - **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): A designer turned developer. Creates stunning UIs. Uses Gemini because its creativity and UI code generation are superior.
186
143
  - **document-writer** (`google/gemini-3-pro-preview`): A technical writing expert. Gemini is a wordsmith; it writes prose that flows naturally.
187
144
 
188
- #### Configuration
189
-
190
- Agents follow the same configuration spec as OpenCode:
191
-
192
- - **Change models**: Override any agent's model via `agents.{name}.model`. See [OpenCode Models](https://opencode.ai/docs/models/#configure-models).
193
- - **Disable MCPs**: Use `disabled_mcps` to turn off built-in MCPs. See [OpenCode MCP Servers](https://opencode.ai/docs/mcp-servers).
194
- - **Disable agents**: Use `disabled_agents` or `agents.{name}.disable`. See [OpenCode Agents](https://opencode.ai/docs/agents).
195
-
196
- While not generally recommended (this plugin is designed for multi-model orchestration), here's an example for Anthropic-only users:
197
-
198
- ```json
199
- {
200
- "agents": {
201
- "explore": { "model": "anthropic/claude-haiku-4-5" },
202
- "frontend-ui-ux-engineer": { "model": "anthropic/claude-opus-4" }
203
- }
204
- }
205
- ```
145
+ Agent models, prompts, and permissions can be customized via `oh-my-opencode.json`. See [Configuration](#configuration) for details.
206
146
 
207
147
  ### Tools
208
148
 
@@ -239,10 +179,97 @@ While not generally recommended (this plugin is designed for multi-model orchest
239
179
  - **websearch_exa**: Exa AI web search. Performs real-time web searches and can scrape content from specific URLs. Returns LLM-optimized context from relevant websites.
240
180
  - **context7**: Library documentation lookup. Fetches up-to-date documentation for any library to assist with accurate coding.
241
181
 
182
+ Don't need these? Disable them via `oh-my-opencode.json`:
183
+
184
+ ```json
185
+ {
186
+ "disabled_mcps": ["websearch_exa"]
187
+ }
188
+ ```
189
+
242
190
  ### Other Features
243
191
 
244
192
  - **Terminal Title**: Auto-updates terminal title with session status (idle ○, processing ◐, tool ⚡, error ✖). Supports tmux.
245
193
 
194
+ ## Configuration
195
+
196
+ Configuration file locations (in priority order):
197
+ 1. `.opencode/oh-my-opencode.json` (project)
198
+ 2. `~/.config/opencode/oh-my-opencode.json` (user)
199
+
200
+ Schema autocomplete is supported:
201
+
202
+ ```json
203
+ {
204
+ "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json"
205
+ }
206
+ ```
207
+
208
+ ### Agents
209
+
210
+ Override built-in agent settings:
211
+
212
+ ```json
213
+ {
214
+ "agents": {
215
+ "explore": {
216
+ "model": "anthropic/claude-haiku-4-5",
217
+ "temperature": 0.5
218
+ },
219
+ "frontend-ui-ux-engineer": {
220
+ "disable": true
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ Each agent supports: `model`, `temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `permission`.
227
+
228
+ Or disable agents via `disabled_agents`:
229
+
230
+ ```json
231
+ {
232
+ "disabled_agents": ["oracle", "frontend-ui-ux-engineer"]
233
+ }
234
+ ```
235
+
236
+ Available agents: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, `document-writer`
237
+
238
+ ### MCPs
239
+
240
+ Disable built-in MCPs:
241
+
242
+ ```json
243
+ {
244
+ "disabled_mcps": ["context7", "websearch_exa"]
245
+ }
246
+ ```
247
+
248
+ See [OpenCode MCP Servers](https://opencode.ai/docs/mcp-servers) for more.
249
+
250
+ ### LSP
251
+
252
+ Oh My OpenCode's LSP tools are for **refactoring only** (rename, code actions). Analysis LSP is handled by OpenCode itself.
253
+
254
+ Configure LSP servers via `lsp` option:
255
+
256
+ ```json
257
+ {
258
+ "lsp": {
259
+ "typescript-language-server": {
260
+ "command": ["typescript-language-server", "--stdio"],
261
+ "extensions": [".ts", ".tsx"],
262
+ "priority": 10
263
+ },
264
+ "pylsp": {
265
+ "disabled": true
266
+ }
267
+ }
268
+ }
269
+ ```
270
+
271
+ Each server supports: `command`, `extensions`, `priority`, `env`, `initialization`, `disabled`.
272
+
246
273
  ## Author's Note
247
274
 
248
275
  Install Oh My OpenCode. Do not waste time configuring OpenCode from scratch.
package/dist/index.js CHANGED
@@ -803,7 +803,7 @@ function detectErrorType(error) {
803
803
  if (message.includes("tool_use") && message.includes("tool_result")) {
804
804
  return "tool_result_missing";
805
805
  }
806
- if (message.includes("thinking") && message.includes("first block")) {
806
+ if (message.includes("thinking") && (message.includes("first block") || message.includes("must start with") || message.includes("preceeding"))) {
807
807
  return "thinking_block_order";
808
808
  }
809
809
  if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
@@ -15258,9 +15258,120 @@ var lsp_code_action_resolve = tool({
15258
15258
  }
15259
15259
  });
15260
15260
  // src/tools/ast-grep/constants.ts
15261
+ import { createRequire as createRequire4 } from "module";
15262
+ import { dirname as dirname2, join as join5 } from "path";
15263
+ import { existsSync as existsSync7 } from "fs";
15264
+
15265
+ // src/tools/ast-grep/downloader.ts
15266
+ var {spawn: spawn4 } = globalThis.Bun;
15267
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, chmodSync as chmodSync2, unlinkSync as unlinkSync2 } from "fs";
15268
+ import { join as join4 } from "path";
15269
+ import { homedir as homedir3 } from "os";
15261
15270
  import { createRequire as createRequire3 } from "module";
15262
- import { dirname as dirname2, join as join4 } from "path";
15263
- import { existsSync as existsSync6 } from "fs";
15271
+ var REPO2 = "ast-grep/ast-grep";
15272
+ var DEFAULT_VERSION = "0.40.0";
15273
+ function getAstGrepVersion() {
15274
+ try {
15275
+ const require2 = createRequire3(import.meta.url);
15276
+ const pkg = require2("@ast-grep/cli/package.json");
15277
+ return pkg.version;
15278
+ } catch {
15279
+ return DEFAULT_VERSION;
15280
+ }
15281
+ }
15282
+ var PLATFORM_MAP2 = {
15283
+ "darwin-arm64": { arch: "aarch64", os: "apple-darwin" },
15284
+ "darwin-x64": { arch: "x86_64", os: "apple-darwin" },
15285
+ "linux-arm64": { arch: "aarch64", os: "unknown-linux-gnu" },
15286
+ "linux-x64": { arch: "x86_64", os: "unknown-linux-gnu" },
15287
+ "win32-x64": { arch: "x86_64", os: "pc-windows-msvc" },
15288
+ "win32-arm64": { arch: "aarch64", os: "pc-windows-msvc" },
15289
+ "win32-ia32": { arch: "i686", os: "pc-windows-msvc" }
15290
+ };
15291
+ function getCacheDir2() {
15292
+ if (process.platform === "win32") {
15293
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
15294
+ const base2 = localAppData || join4(homedir3(), "AppData", "Local");
15295
+ return join4(base2, "oh-my-opencode", "bin");
15296
+ }
15297
+ const xdgCache = process.env.XDG_CACHE_HOME;
15298
+ const base = xdgCache || join4(homedir3(), ".cache");
15299
+ return join4(base, "oh-my-opencode", "bin");
15300
+ }
15301
+ function getBinaryName3() {
15302
+ return process.platform === "win32" ? "sg.exe" : "sg";
15303
+ }
15304
+ function getCachedBinaryPath2() {
15305
+ const binaryPath = join4(getCacheDir2(), getBinaryName3());
15306
+ return existsSync6(binaryPath) ? binaryPath : null;
15307
+ }
15308
+ async function extractZip2(archivePath, destDir) {
15309
+ const proc = process.platform === "win32" ? spawn4([
15310
+ "powershell",
15311
+ "-command",
15312
+ `Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force`
15313
+ ], { stdout: "pipe", stderr: "pipe" }) : spawn4(["unzip", "-o", archivePath, "-d", destDir], { stdout: "pipe", stderr: "pipe" });
15314
+ const exitCode = await proc.exited;
15315
+ if (exitCode !== 0) {
15316
+ const stderr = await new Response(proc.stderr).text();
15317
+ const toolHint = process.platform === "win32" ? "Ensure PowerShell is available on your system." : "Please install 'unzip' (e.g., apt install unzip, brew install unzip).";
15318
+ throw new Error(`zip extraction failed (exit ${exitCode}): ${stderr}
15319
+
15320
+ ${toolHint}`);
15321
+ }
15322
+ }
15323
+ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
15324
+ const platformKey = `${process.platform}-${process.arch}`;
15325
+ const platformInfo = PLATFORM_MAP2[platformKey];
15326
+ if (!platformInfo) {
15327
+ console.error(`[oh-my-opencode] Unsupported platform for ast-grep: ${platformKey}`);
15328
+ return null;
15329
+ }
15330
+ const cacheDir = getCacheDir2();
15331
+ const binaryName = getBinaryName3();
15332
+ const binaryPath = join4(cacheDir, binaryName);
15333
+ if (existsSync6(binaryPath)) {
15334
+ return binaryPath;
15335
+ }
15336
+ const { arch, os } = platformInfo;
15337
+ const assetName = `app-${arch}-${os}.zip`;
15338
+ const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
15339
+ console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
15340
+ try {
15341
+ if (!existsSync6(cacheDir)) {
15342
+ mkdirSync2(cacheDir, { recursive: true });
15343
+ }
15344
+ const response = await fetch(downloadUrl, { redirect: "follow" });
15345
+ if (!response.ok) {
15346
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
15347
+ }
15348
+ const archivePath = join4(cacheDir, assetName);
15349
+ const arrayBuffer = await response.arrayBuffer();
15350
+ await Bun.write(archivePath, arrayBuffer);
15351
+ await extractZip2(archivePath, cacheDir);
15352
+ if (existsSync6(archivePath)) {
15353
+ unlinkSync2(archivePath);
15354
+ }
15355
+ if (process.platform !== "win32" && existsSync6(binaryPath)) {
15356
+ chmodSync2(binaryPath, 493);
15357
+ }
15358
+ console.log(`[oh-my-opencode] ast-grep binary ready.`);
15359
+ return binaryPath;
15360
+ } catch (err) {
15361
+ console.error(`[oh-my-opencode] Failed to download ast-grep: ${err instanceof Error ? err.message : err}`);
15362
+ return null;
15363
+ }
15364
+ }
15365
+ async function ensureAstGrepBinary() {
15366
+ const cachedPath = getCachedBinaryPath2();
15367
+ if (cachedPath) {
15368
+ return cachedPath;
15369
+ }
15370
+ const version2 = getAstGrepVersion();
15371
+ return downloadAstGrep(version2);
15372
+ }
15373
+
15374
+ // src/tools/ast-grep/constants.ts
15264
15375
  function getPlatformPackageName2() {
15265
15376
  const platform = process.platform;
15266
15377
  const arch = process.arch;
@@ -15275,32 +15386,60 @@ function getPlatformPackageName2() {
15275
15386
  };
15276
15387
  return platformMap[`${platform}-${arch}`] ?? null;
15277
15388
  }
15278
- function findSgCliPath() {
15389
+ function findSgCliPathSync() {
15390
+ const binaryName = process.platform === "win32" ? "sg.exe" : "sg";
15279
15391
  try {
15280
- const require2 = createRequire3(import.meta.url);
15392
+ const require2 = createRequire4(import.meta.url);
15281
15393
  const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
15282
15394
  const cliDir = dirname2(cliPkgPath);
15283
- const sgPath = join4(cliDir, process.platform === "win32" ? "sg.exe" : "sg");
15284
- if (existsSync6(sgPath)) {
15395
+ const sgPath = join5(cliDir, binaryName);
15396
+ if (existsSync7(sgPath)) {
15285
15397
  return sgPath;
15286
15398
  }
15287
15399
  } catch {}
15288
15400
  const platformPkg = getPlatformPackageName2();
15289
15401
  if (platformPkg) {
15290
15402
  try {
15291
- const require2 = createRequire3(import.meta.url);
15403
+ const require2 = createRequire4(import.meta.url);
15292
15404
  const pkgPath = require2.resolve(`${platformPkg}/package.json`);
15293
15405
  const pkgDir = dirname2(pkgPath);
15294
- const binaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
15295
- const binaryPath = join4(pkgDir, binaryName);
15296
- if (existsSync6(binaryPath)) {
15406
+ const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
15407
+ const binaryPath = join5(pkgDir, astGrepName);
15408
+ if (existsSync7(binaryPath)) {
15297
15409
  return binaryPath;
15298
15410
  }
15299
15411
  } catch {}
15300
15412
  }
15413
+ if (process.platform === "darwin") {
15414
+ const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
15415
+ for (const path of homebrewPaths) {
15416
+ if (existsSync7(path)) {
15417
+ return path;
15418
+ }
15419
+ }
15420
+ }
15421
+ const cachedPath = getCachedBinaryPath2();
15422
+ if (cachedPath) {
15423
+ return cachedPath;
15424
+ }
15425
+ return null;
15426
+ }
15427
+ var resolvedCliPath2 = null;
15428
+ function getSgCliPath() {
15429
+ if (resolvedCliPath2 !== null) {
15430
+ return resolvedCliPath2;
15431
+ }
15432
+ const syncPath = findSgCliPathSync();
15433
+ if (syncPath) {
15434
+ resolvedCliPath2 = syncPath;
15435
+ return syncPath;
15436
+ }
15301
15437
  return "sg";
15302
15438
  }
15303
- var SG_CLI_PATH = findSgCliPath();
15439
+ function setSgCliPath(path) {
15440
+ resolvedCliPath2 = path;
15441
+ }
15442
+ var SG_CLI_PATH = getSgCliPath();
15304
15443
  var CLI_LANGUAGES = [
15305
15444
  "bash",
15306
15445
  "c",
@@ -15358,7 +15497,44 @@ var LANG_EXTENSIONS = {
15358
15497
  };
15359
15498
 
15360
15499
  // src/tools/ast-grep/cli.ts
15361
- var {spawn: spawn4 } = globalThis.Bun;
15500
+ var {spawn: spawn5 } = globalThis.Bun;
15501
+ import { existsSync as existsSync8 } from "fs";
15502
+ var resolvedCliPath3 = null;
15503
+ var initPromise2 = null;
15504
+ async function getAstGrepPath() {
15505
+ if (resolvedCliPath3 !== null && existsSync8(resolvedCliPath3)) {
15506
+ return resolvedCliPath3;
15507
+ }
15508
+ if (initPromise2) {
15509
+ return initPromise2;
15510
+ }
15511
+ initPromise2 = (async () => {
15512
+ const syncPath = findSgCliPathSync();
15513
+ if (syncPath && existsSync8(syncPath)) {
15514
+ resolvedCliPath3 = syncPath;
15515
+ setSgCliPath(syncPath);
15516
+ return syncPath;
15517
+ }
15518
+ const downloadedPath = await ensureAstGrepBinary();
15519
+ if (downloadedPath) {
15520
+ resolvedCliPath3 = downloadedPath;
15521
+ setSgCliPath(downloadedPath);
15522
+ return downloadedPath;
15523
+ }
15524
+ return null;
15525
+ })();
15526
+ return initPromise2;
15527
+ }
15528
+ async function spawnSg(cliPath, args) {
15529
+ const proc = spawn5([cliPath, ...args], {
15530
+ stdout: "pipe",
15531
+ stderr: "pipe"
15532
+ });
15533
+ const stdout = await new Response(proc.stdout).text();
15534
+ const stderr = await new Response(proc.stderr).text();
15535
+ const exitCode = await proc.exited;
15536
+ return { stdout, stderr, exitCode };
15537
+ }
15362
15538
  async function runSg(options) {
15363
15539
  const args = ["run", "-p", options.pattern, "--lang", options.lang, "--json=compact"];
15364
15540
  if (options.rewrite) {
@@ -15377,13 +15553,37 @@ async function runSg(options) {
15377
15553
  }
15378
15554
  const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
15379
15555
  args.push(...paths);
15380
- const proc = spawn4([SG_CLI_PATH, ...args], {
15381
- stdout: "pipe",
15382
- stderr: "pipe"
15383
- });
15384
- const stdout = await new Response(proc.stdout).text();
15385
- const stderr = await new Response(proc.stderr).text();
15386
- const exitCode = await proc.exited;
15556
+ let cliPath = getSgCliPath();
15557
+ if (!existsSync8(cliPath) && cliPath !== "sg") {
15558
+ const downloadedPath = await getAstGrepPath();
15559
+ if (downloadedPath) {
15560
+ cliPath = downloadedPath;
15561
+ }
15562
+ }
15563
+ let result;
15564
+ try {
15565
+ result = await spawnSg(cliPath, args);
15566
+ } catch (e) {
15567
+ const error45 = e;
15568
+ if (error45.code === "ENOENT" || error45.message?.includes("ENOENT") || error45.message?.includes("not found")) {
15569
+ const downloadedPath = await ensureAstGrepBinary();
15570
+ if (downloadedPath) {
15571
+ resolvedCliPath3 = downloadedPath;
15572
+ setSgCliPath(downloadedPath);
15573
+ result = await spawnSg(downloadedPath, args);
15574
+ } else {
15575
+ throw new Error(`ast-grep CLI binary not found.
15576
+
15577
+ ` + `Auto-download failed. Manual install options:
15578
+ ` + ` bun add -D @ast-grep/cli
15579
+ ` + ` cargo install ast-grep --locked
15580
+ ` + ` brew install ast-grep`);
15581
+ }
15582
+ } else {
15583
+ throw new Error(`Failed to spawn ast-grep: ${error45.message}`);
15584
+ }
15585
+ }
15586
+ const { stdout, stderr, exitCode } = result;
15387
15587
  if (exitCode !== 0 && stdout.trim() === "") {
15388
15588
  if (stderr.includes("No files found")) {
15389
15589
  return [];
@@ -15413,7 +15613,15 @@ var LANG_MAP = {
15413
15613
  typescript: Lang.TypeScript
15414
15614
  };
15415
15615
  function parseCode(code, lang) {
15416
- return parse5(LANG_MAP[lang], code);
15616
+ const parseLang = LANG_MAP[lang];
15617
+ if (!parseLang) {
15618
+ const supportedLangs = NAPI_LANGUAGES.join(", ");
15619
+ throw new Error(`Unsupported language for NAPI: "${lang}"
15620
+ ` + `Supported languages: ${supportedLangs}
15621
+
15622
+ ` + `Use ast_grep_search for other languages (25 supported via CLI).`);
15623
+ }
15624
+ return parse5(parseLang, code);
15417
15625
  }
15418
15626
  function findPattern(root, pattern) {
15419
15627
  return root.root().findAll(pattern);
@@ -15696,13 +15904,12 @@ var ast_grep_transform = tool({
15696
15904
  }
15697
15905
  }
15698
15906
  });
15699
-
15700
15907
  // src/tools/safe-grep/cli.ts
15701
- var {spawn: spawn5 } = globalThis.Bun;
15908
+ var {spawn: spawn6 } = globalThis.Bun;
15702
15909
 
15703
15910
  // src/tools/safe-grep/constants.ts
15704
- import { existsSync as existsSync7 } from "fs";
15705
- import { join as join5, dirname as dirname3 } from "path";
15911
+ import { existsSync as existsSync9 } from "fs";
15912
+ import { join as join6, dirname as dirname3 } from "path";
15706
15913
  import { spawnSync } from "child_process";
15707
15914
  var cachedCli = null;
15708
15915
  function findExecutable(name) {
@@ -15723,13 +15930,13 @@ function getOpenCodeBundledRg() {
15723
15930
  const isWindows = process.platform === "win32";
15724
15931
  const rgName = isWindows ? "rg.exe" : "rg";
15725
15932
  const candidates = [
15726
- join5(execDir, rgName),
15727
- join5(execDir, "bin", rgName),
15728
- join5(execDir, "..", "bin", rgName),
15729
- join5(execDir, "..", "libexec", rgName)
15933
+ join6(execDir, rgName),
15934
+ join6(execDir, "bin", rgName),
15935
+ join6(execDir, "..", "bin", rgName),
15936
+ join6(execDir, "..", "libexec", rgName)
15730
15937
  ];
15731
15938
  for (const candidate of candidates) {
15732
- if (existsSync7(candidate)) {
15939
+ if (existsSync9(candidate)) {
15733
15940
  return candidate;
15734
15941
  }
15735
15942
  }
@@ -15870,7 +16077,7 @@ async function runRg(options) {
15870
16077
  }
15871
16078
  const paths = options.paths?.length ? options.paths : ["."];
15872
16079
  args.push(...paths);
15873
- const proc = spawn5([cli.path, ...args], {
16080
+ const proc = spawn6([cli.path, ...args], {
15874
16081
  stdout: "pipe",
15875
16082
  stderr: "pipe"
15876
16083
  });
@@ -16168,7 +16375,14 @@ var OhMyOpenCodePlugin = async (ctx) => {
16168
16375
  sessionID,
16169
16376
  error: error45
16170
16377
  };
16171
- await sessionRecovery.handleSessionRecovery(messageInfo);
16378
+ const recovered = await sessionRecovery.handleSessionRecovery(messageInfo);
16379
+ if (recovered && sessionID && sessionID === mainSessionID) {
16380
+ await ctx.client.session.prompt({
16381
+ path: { id: sessionID },
16382
+ body: { parts: [{ type: "text", text: "continue" }] },
16383
+ query: { directory: ctx.directory }
16384
+ }).catch(() => {});
16385
+ }
16172
16386
  }
16173
16387
  if (sessionID && sessionID === mainSessionID) {
16174
16388
  updateTerminalTitle({
@@ -8,4 +8,8 @@ export interface RunOptions {
8
8
  context?: number;
9
9
  updateAll?: boolean;
10
10
  }
11
+ export declare function getAstGrepPath(): Promise<string | null>;
12
+ export declare function startBackgroundInit(): void;
11
13
  export declare function runSg(options: RunOptions): Promise<CliMatch[]>;
14
+ export declare function isCliAvailable(): boolean;
15
+ export declare function ensureCliAvailable(): Promise<boolean>;
@@ -1,4 +1,27 @@
1
+ export declare function findSgCliPathSync(): string | null;
2
+ export declare function getSgCliPath(): string;
3
+ export declare function setSgCliPath(path: string): void;
1
4
  export declare const SG_CLI_PATH: string;
2
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"];
3
6
  export declare const NAPI_LANGUAGES: readonly ["html", "javascript", "tsx", "css", "typescript"];
4
7
  export declare const LANG_EXTENSIONS: Record<string, string[]>;
8
+ export interface EnvironmentCheckResult {
9
+ cli: {
10
+ available: boolean;
11
+ path: string;
12
+ error?: string;
13
+ };
14
+ napi: {
15
+ available: boolean;
16
+ error?: string;
17
+ };
18
+ }
19
+ /**
20
+ * Check if ast-grep CLI and NAPI are available.
21
+ * Call this at startup to provide early feedback about missing dependencies.
22
+ */
23
+ export declare function checkEnvironment(): EnvironmentCheckResult;
24
+ /**
25
+ * Format environment check result as user-friendly message.
26
+ */
27
+ export declare function formatEnvironmentCheck(result: EnvironmentCheckResult): string;
@@ -0,0 +1,5 @@
1
+ export declare function getCacheDir(): string;
2
+ export declare function getBinaryName(): string;
3
+ export declare function getCachedBinaryPath(): string | null;
4
+ export declare function downloadAstGrep(version?: string): Promise<string | null>;
5
+ export declare function ensureAstGrepBinary(): Promise<string | null>;
@@ -89,4 +89,8 @@ export declare const builtinTools: {
89
89
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
90
90
  };
91
91
  };
92
- export { ast_grep_search, ast_grep_replace, };
92
+ export { ast_grep_search, ast_grep_replace };
93
+ export { ensureAstGrepBinary, getCachedBinaryPath, getCacheDir } from "./downloader";
94
+ export { getAstGrepPath, isCliAvailable, ensureCliAvailable, startBackgroundInit } from "./cli";
95
+ export { checkEnvironment, formatEnvironmentCheck } from "./constants";
96
+ export type { EnvironmentCheckResult } from "./constants";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
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",
@@ -1,159 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/dist/oh-my-opencode.schema.json",
4
- "title": "Oh My OpenCode Configuration",
5
- "description": "Configuration schema for oh-my-opencode plugin",
6
- "type": "object",
7
- "properties": {
8
- "$schema": {
9
- "type": "string"
10
- },
11
- "disabled_mcps": {
12
- "type": "array",
13
- "items": {
14
- "type": "string",
15
- "enum": [
16
- "websearch_exa",
17
- "context7"
18
- ]
19
- }
20
- },
21
- "disabled_agents": {
22
- "type": "array",
23
- "items": {
24
- "type": "string",
25
- "enum": [
26
- "oracle",
27
- "librarian",
28
- "explore",
29
- "frontend-ui-ux-engineer",
30
- "document-writer"
31
- ]
32
- }
33
- },
34
- "agents": {
35
- "type": "object",
36
- "propertyNames": {
37
- "type": "string",
38
- "enum": [
39
- "oracle",
40
- "librarian",
41
- "explore",
42
- "frontend-ui-ux-engineer",
43
- "document-writer"
44
- ]
45
- },
46
- "additionalProperties": {
47
- "type": "object",
48
- "properties": {
49
- "model": {
50
- "type": "string"
51
- },
52
- "temperature": {
53
- "type": "number",
54
- "minimum": 0,
55
- "maximum": 2
56
- },
57
- "top_p": {
58
- "type": "number",
59
- "minimum": 0,
60
- "maximum": 1
61
- },
62
- "prompt": {
63
- "type": "string"
64
- },
65
- "tools": {
66
- "type": "object",
67
- "propertyNames": {
68
- "type": "string"
69
- },
70
- "additionalProperties": {
71
- "type": "boolean"
72
- }
73
- },
74
- "disable": {
75
- "type": "boolean"
76
- },
77
- "description": {
78
- "type": "string"
79
- },
80
- "mode": {
81
- "type": "string",
82
- "enum": [
83
- "subagent",
84
- "primary",
85
- "all"
86
- ]
87
- },
88
- "color": {
89
- "type": "string",
90
- "pattern": "^#[0-9A-Fa-f]{6}$"
91
- },
92
- "permission": {
93
- "type": "object",
94
- "properties": {
95
- "edit": {
96
- "type": "string",
97
- "enum": [
98
- "ask",
99
- "allow",
100
- "deny"
101
- ]
102
- },
103
- "bash": {
104
- "anyOf": [
105
- {
106
- "type": "string",
107
- "enum": [
108
- "ask",
109
- "allow",
110
- "deny"
111
- ]
112
- },
113
- {
114
- "type": "object",
115
- "propertyNames": {
116
- "type": "string"
117
- },
118
- "additionalProperties": {
119
- "type": "string",
120
- "enum": [
121
- "ask",
122
- "allow",
123
- "deny"
124
- ]
125
- }
126
- }
127
- ]
128
- },
129
- "webfetch": {
130
- "type": "string",
131
- "enum": [
132
- "ask",
133
- "allow",
134
- "deny"
135
- ]
136
- },
137
- "doom_loop": {
138
- "type": "string",
139
- "enum": [
140
- "ask",
141
- "allow",
142
- "deny"
143
- ]
144
- },
145
- "external_directory": {
146
- "type": "string",
147
- "enum": [
148
- "ask",
149
- "allow",
150
- "deny"
151
- ]
152
- }
153
- }
154
- }
155
- }
156
- }
157
- }
158
- }
159
- }