oh-my-opencode 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +51 -20
- package/README.ko.md +52 -21
- package/README.md +53 -20
- package/README.zh-cn.md +51 -20
- package/dist/config/schema.d.ts +1 -0
- package/dist/hooks/auto-update-checker/checker.d.ts +7 -0
- package/dist/hooks/auto-update-checker/index.d.ts +1 -1
- package/dist/hooks/auto-update-checker/types.d.ts +1 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/index.js +348 -178
- package/package.json +3 -2
- package/dist/hooks/grep-output-truncator.d.ts +0 -12
package/README.ja.md
CHANGED
|
@@ -156,11 +156,22 @@ Windows から Linux に初めて乗り換えた時のこと、自分の思い
|
|
|
156
156
|
|
|
157
157
|
### ステップ 0: セットアップについてユーザーに尋ねる
|
|
158
158
|
|
|
159
|
-
OpenCode は様々なモデルをサポートしており、当プラグインは様々な最先端 LLM
|
|
159
|
+
OpenCode は様々なモデルをサポートしており、当プラグインは様々な最先端 LLM の使用を提案しています。そのため、ユーザーに以下の質問をする必要があります。
|
|
160
|
+
|
|
161
|
+
**重要**: ユーザーがどのプロバイダーを持っているか追跡してください。ユーザーが Claude を持っていると確認した場合のみ、`anthropic/claude-opus-4-5` をフォールバックとして使用できます。Claude がない場合は、代わりに `opencode/big-pickle` をフォールバックとして使用してください。
|
|
160
162
|
|
|
161
163
|
1. **Claude Pro/Max のサブスクリプションをお持ちですか?** お持ちでない場合、メインオーケストレーターモデルとして 'opencode/big-pickle' (GLM 4.6 として知られる) を使用することになります (非推奨)。
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
+
- ユーザーが**「はい」**と答えた場合、追加質問: **max20 (20x モード) を使用していますか?**
|
|
165
|
+
- **「はい」 (max20)**: ToDo 追加: "ユーザーの Claude Pro/Max 認証設定" — librarian に Claude Sonnet 4.5 をフル使用。**記憶: Claude がフォールバックとして使用可能。**
|
|
166
|
+
- **「いいえ」 (max20 ではない)**: ToDo 追加: "ユーザーの Claude Pro/Max 認証設定"、そして `~/.config/opencode/oh-my-opencode.json` に以下をマージ。**記憶: Claude がフォールバックとして使用可能。**
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"agents": {
|
|
170
|
+
"librarian": { "model": "opencode/big-pickle" }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
- ユーザーが**「いいえ」** (Claude サブスクリプションなし) と答えた場合、`~/.config/opencode/oh-my-opencode.json` に以下を保存。**記憶: Claude は使用不可 — すべてのフォールバックは `opencode/big-pickle` を使用する必要があります。**
|
|
164
175
|
```json
|
|
165
176
|
{
|
|
166
177
|
"agents": {
|
|
@@ -170,29 +181,49 @@ OpenCode は様々なモデルをサポートしており、当プラグイン
|
|
|
170
181
|
}
|
|
171
182
|
```
|
|
172
183
|
|
|
173
|
-
2. **ChatGPT のサブスクリプションをお持ちですか?** お持ちでない場合、oracle
|
|
184
|
+
2. **ChatGPT のサブスクリプションをお持ちですか?** お持ちでない場合、oracle エージェントにフォールバックモデルを使用することになります。
|
|
174
185
|
- ユーザーが**「はい」**と答えた場合、ToDo 追加: "ユーザーの ChatGPT 認証設定"
|
|
175
|
-
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"
|
|
186
|
+
- ユーザーが**「いいえ」**と答えた場合:
|
|
187
|
+
- **Claude が使用可能な場合**: `oh-my-opencode.json` に以下をマージ:
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"agents": {
|
|
191
|
+
"oracle": { "model": "anthropic/claude-opus-4-5" }
|
|
192
|
+
}
|
|
180
193
|
}
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
```
|
|
195
|
+
- **Claude が使用不可の場合**: `oh-my-opencode.json` に以下をマージ:
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"agents": {
|
|
199
|
+
"oracle": { "model": "opencode/big-pickle" }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
183
203
|
|
|
184
204
|
3. **Gemini モデルを統合しますか?** 統合しない場合、美しいデザインを享受できません。
|
|
185
205
|
- ユーザーが**「はい」**と答えた場合、ToDo 追加: "Google OAuth 設定"
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
"
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
- ユーザーが**「いいえ」**と答えた場合:
|
|
207
|
+
- **Claude が使用可能な場合**: `oh-my-opencode.json` に以下をマージ:
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"agents": {
|
|
211
|
+
"frontend-ui-ux-engineer": { "model": "anthropic/claude-opus-4-5" },
|
|
212
|
+
"document-writer": { "model": "anthropic/claude-opus-4-5" },
|
|
213
|
+
"multimodal-looker": { "model": "anthropic/claude-opus-4-5" }
|
|
214
|
+
}
|
|
193
215
|
}
|
|
194
|
-
|
|
195
|
-
|
|
216
|
+
```
|
|
217
|
+
- **Claude が使用不可の場合**: `oh-my-opencode.json` に以下をマージ:
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"agents": {
|
|
221
|
+
"frontend-ui-ux-engineer": { "model": "opencode/big-pickle" },
|
|
222
|
+
"document-writer": { "model": "opencode/big-pickle" },
|
|
223
|
+
"multimodal-looker": { "model": "opencode/big-pickle" }
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
196
227
|
|
|
197
228
|
### ステップ 1: OpenCode インストール確認
|
|
198
229
|
|
package/README.ko.md
CHANGED
|
@@ -153,43 +153,74 @@ OpenCode 가 낭만이 사라진것같은 오늘날의 시대에, 당신에게
|
|
|
153
153
|
|
|
154
154
|
### 0단계: 사용자에게 설정에 대해 물어보세요.
|
|
155
155
|
|
|
156
|
-
OpenCode는 다양한 모델을 지원하며, 이 플러그인은 다양한 프론티어 LLM 사용을 제안합니다. 따라서 사용자에게 다음 질문을 해야
|
|
156
|
+
OpenCode는 다양한 모델을 지원하며, 이 플러그인은 다양한 프론티어 LLM 사용을 제안합니다. 따라서 사용자에게 다음 질문을 해야 합니다.
|
|
157
|
+
|
|
158
|
+
**중요**: 사용자가 어떤 프로바이더를 가지고 있는지 추적하세요. 사용자가 Claude가 있다고 확인한 경우에만 `anthropic/claude-opus-4-5`를 폴백으로 사용할 수 있습니다. Claude가 없다면 `opencode/big-pickle`을 폴백으로 사용해야 합니다.
|
|
157
159
|
|
|
158
160
|
1. **Claude Pro/Max 구독이 있으신가요?** 그렇지 않다면 메인 오케스트레이터 모델로 'opencode/big-pickle'(GLM 4.6으로 알려짐)을 사용하게 됩니다 (권장하지 않음).
|
|
159
|
-
- 사용자가 **'예'**라고 답하면,
|
|
160
|
-
|
|
161
|
+
- 사용자가 **'예'**라고 답하면, 추가 질문: **max20 (20x 모드)를 사용하고 계신가요?**
|
|
162
|
+
- **'예' (max20)**: 할 일 추가: "사용자 Claude Pro/Max 인증 설정" — librarian에 Claude Sonnet 4.5 전체 사용. **기억: Claude를 폴백으로 사용 가능.**
|
|
163
|
+
- **'아니오' (max20 아님)**: 할 일 추가: "사용자 Claude Pro/Max 인증 설정", 그리고 `~/.config/opencode/oh-my-opencode.json`에 다음을 병합. **기억: Claude를 폴백으로 사용 가능.**
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"agents": {
|
|
167
|
+
"librarian": { "model": "opencode/big-pickle" }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
- 사용자가 **'아니오'** (Claude 구독 없음)라고 답하면, `~/.config/opencode/oh-my-opencode.json`에 다음을 저장. **기억: Claude 사용 불가 — 모든 폴백은 `opencode/big-pickle`을 사용해야 함.**
|
|
161
172
|
```json
|
|
162
173
|
{
|
|
163
174
|
"agents": {
|
|
164
|
-
"
|
|
175
|
+
"Sisyphus": { "model": "opencode/big-pickle" },
|
|
165
176
|
"librarian": { "model": "opencode/big-pickle" }
|
|
166
177
|
}
|
|
167
178
|
}
|
|
168
179
|
```
|
|
169
180
|
|
|
170
|
-
2. **ChatGPT 구독이 있으신가요?** 그렇지 않다면 oracle 에이전트에
|
|
181
|
+
2. **ChatGPT 구독이 있으신가요?** 그렇지 않다면 oracle 에이전트에 폴백 모델을 사용하게 됩니다.
|
|
171
182
|
- 사용자가 **'예'**라고 답하면, 할 일 추가: "사용자 ChatGPT 인증 설정"
|
|
172
|
-
- 사용자가 **'아니오'**라고
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
"
|
|
183
|
+
- 사용자가 **'아니오'**라고 답하면:
|
|
184
|
+
- **Claude가 사용 가능한 경우**: `oh-my-opencode.json`에 다음을 병합:
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"agents": {
|
|
188
|
+
"oracle": { "model": "anthropic/claude-opus-4-5" }
|
|
189
|
+
}
|
|
177
190
|
}
|
|
178
|
-
|
|
179
|
-
|
|
191
|
+
```
|
|
192
|
+
- **Claude가 사용 불가능한 경우**: `oh-my-opencode.json`에 다음을 병합:
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"agents": {
|
|
196
|
+
"oracle": { "model": "opencode/big-pickle" }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
180
200
|
|
|
181
201
|
3. **Gemini 모델을 통합하시겠습니까?** 그렇지 않다면 아름다운 디자인을 즐길 수 없습니다.
|
|
182
202
|
- 사용자가 **'예'**라고 답하면, 할 일 추가: "Google OAuth 설정"
|
|
183
|
-
- 사용자가 **'아니오'**라고
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"
|
|
188
|
-
|
|
189
|
-
|
|
203
|
+
- 사용자가 **'아니오'**라고 답하면:
|
|
204
|
+
- **Claude가 사용 가능한 경우**: `oh-my-opencode.json`에 다음을 병합:
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"agents": {
|
|
208
|
+
"frontend-ui-ux-engineer": { "model": "anthropic/claude-opus-4-5" },
|
|
209
|
+
"document-writer": { "model": "anthropic/claude-opus-4-5" },
|
|
210
|
+
"multimodal-looker": { "model": "anthropic/claude-opus-4-5" }
|
|
211
|
+
}
|
|
190
212
|
}
|
|
191
|
-
|
|
192
|
-
|
|
213
|
+
```
|
|
214
|
+
- **Claude가 사용 불가능한 경우**: `oh-my-opencode.json`에 다음을 병합:
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"agents": {
|
|
218
|
+
"frontend-ui-ux-engineer": { "model": "opencode/big-pickle" },
|
|
219
|
+
"document-writer": { "model": "opencode/big-pickle" },
|
|
220
|
+
"multimodal-looker": { "model": "opencode/big-pickle" }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
193
224
|
|
|
194
225
|
### 1단계: OpenCode 설치 확인
|
|
195
226
|
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
NOTICE: News regarding oh-my-opencode used to posted on my X account, but since it got suspended innocently, my friend [@_junhoyeo](https://x.com/_junhoyeo) will post on behalf of me. (THE GUY WHO MADE HERO IMAGE)
|
|
2
|
+
|
|
1
3
|
<!-- <CENTERED SECTION FOR GITHUB DISPLAY> -->
|
|
2
4
|
|
|
3
5
|
<div align="center">
|
|
@@ -181,11 +183,22 @@ follow these steps:
|
|
|
181
183
|
|
|
182
184
|
### Step 0: Ask user about the setup.
|
|
183
185
|
|
|
184
|
-
Since OpenCode
|
|
186
|
+
Since OpenCode supports various models, and our plugin suggests various frontier LLMs, you must ask user the following questions.
|
|
187
|
+
|
|
188
|
+
**IMPORTANT**: Track which providers the user has. Only use `anthropic/claude-opus-4-5` as fallback when the user confirmed they have Claude. If they don't have Claude, use `opencode/big-pickle` as fallback instead.
|
|
185
189
|
|
|
186
190
|
1. **Do you have a Claude Pro/Max Subscription?** Otherwise, we'll use 'opencode/big-pickle' (known for GLM 4.6) as a main orchestrator model (not recommended)
|
|
187
|
-
- If user said **yes**,
|
|
188
|
-
|
|
191
|
+
- If user said **yes**, ask follow-up: **Are you on max20 (20x mode)?**
|
|
192
|
+
- If **yes (max20)**: Add ToDo: "Setup user Claude Pro/Max Auth" — full Claude Sonnet 4.5 for librarian. **Remember: Claude is available for fallbacks.**
|
|
193
|
+
- If **no (not max20)**: Add ToDo: "Setup user Claude Pro/Max Auth", then merge the following to `~/.config/opencode/oh-my-opencode.json`. **Remember: Claude is available for fallbacks.**
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"agents": {
|
|
197
|
+
"librarian": { "model": "opencode/big-pickle" }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
- If user said **no** (no Claude subscription), merge the following to `~/.config/opencode/oh-my-opencode.json`. **Remember: Claude is NOT available — all fallbacks must use `opencode/big-pickle`.**
|
|
189
202
|
```json
|
|
190
203
|
{
|
|
191
204
|
"agents": {
|
|
@@ -195,29 +208,49 @@ Since OpenCode Supports various models, and our plugin suggests various frontier
|
|
|
195
208
|
}
|
|
196
209
|
```
|
|
197
210
|
|
|
198
|
-
2. **Do you have a ChatGPT Subscription?** Otherwise, we'll use
|
|
211
|
+
2. **Do you have a ChatGPT Subscription?** Otherwise, we'll use a fallback model for oracle agent.
|
|
199
212
|
- If user said **yes**, add ToDo: "Setup user ChatGPT Auth"
|
|
200
|
-
- If user said **no
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"
|
|
213
|
+
- If user said **no**:
|
|
214
|
+
- **If Claude is available**: merge the following to `oh-my-opencode.json`:
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"agents": {
|
|
218
|
+
"oracle": { "model": "anthropic/claude-opus-4-5" }
|
|
219
|
+
}
|
|
205
220
|
}
|
|
206
|
-
|
|
207
|
-
|
|
221
|
+
```
|
|
222
|
+
- **If Claude is NOT available**: merge the following to `oh-my-opencode.json`:
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"agents": {
|
|
226
|
+
"oracle": { "model": "opencode/big-pickle" }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
208
230
|
|
|
209
231
|
3. **Will you integrate Gemini models?** Otherwise, you cannot enjoy the beautiful design.
|
|
210
232
|
- If user said **yes**, add ToDo: "Setup Google OAuth"
|
|
211
|
-
- If user said **no
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"
|
|
216
|
-
|
|
217
|
-
|
|
233
|
+
- If user said **no**:
|
|
234
|
+
- **If Claude is available**: merge the following to `oh-my-opencode.json`:
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"agents": {
|
|
238
|
+
"frontend-ui-ux-engineer": { "model": "anthropic/claude-opus-4-5" },
|
|
239
|
+
"document-writer": { "model": "anthropic/claude-opus-4-5" },
|
|
240
|
+
"multimodal-looker": { "model": "anthropic/claude-opus-4-5" }
|
|
241
|
+
}
|
|
218
242
|
}
|
|
219
|
-
|
|
220
|
-
|
|
243
|
+
```
|
|
244
|
+
- **If Claude is NOT available**: merge the following to `oh-my-opencode.json`:
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"agents": {
|
|
248
|
+
"frontend-ui-ux-engineer": { "model": "opencode/big-pickle" },
|
|
249
|
+
"document-writer": { "model": "opencode/big-pickle" },
|
|
250
|
+
"multimodal-looker": { "model": "opencode/big-pickle" }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
221
254
|
|
|
222
255
|
### Step 1: Install OpenCode, if not
|
|
223
256
|
|
package/README.zh-cn.md
CHANGED
|
@@ -162,11 +162,22 @@ Install and configure by following the instructions here https://raw.githubuserc
|
|
|
162
162
|
|
|
163
163
|
### 步骤 0:摸清用户底细
|
|
164
164
|
|
|
165
|
-
OpenCode 支持各种模型,我们这个插件也建议用各种前沿 LLM
|
|
165
|
+
OpenCode 支持各种模型,我们这个插件也建议用各种前沿 LLM。所以先问清楚。
|
|
166
|
+
|
|
167
|
+
**重要**:追踪用户有哪些 provider。只有用户确认有 Claude 的情况下,才能用 `anthropic/claude-opus-4-5` 做 fallback。没有 Claude 就用 `opencode/big-pickle` 做 fallback。
|
|
166
168
|
|
|
167
169
|
1. **有 Claude Pro/Max 订阅吗?** 没有的话,主编排模型只能用 'opencode/big-pickle'(也就是 GLM 4.6),这体验可不太好(不推荐)。
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
+
- 用户说**有**,继续问:**你用的是 max20 (20x 模式) 吗?**
|
|
171
|
+
- **是 (max20)**:加个待办:"配置用户 Claude Pro/Max 认证" — librarian 用完整的 Claude Sonnet 4.5。**记住:Claude 可以做 fallback。**
|
|
172
|
+
- **不是 (没有 max20)**:加个待办:"配置用户 Claude Pro/Max 认证",然后把这个合并到 `~/.config/opencode/oh-my-opencode.json`。**记住:Claude 可以做 fallback。**
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"agents": {
|
|
176
|
+
"librarian": { "model": "opencode/big-pickle" }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
- 用户说**没有** (没有 Claude 订阅),把这坨配置写到 `~/.config/opencode/oh-my-opencode.json`。**记住:没有 Claude — 所有 fallback 都得用 `opencode/big-pickle`。**
|
|
170
181
|
```json
|
|
171
182
|
{
|
|
172
183
|
"agents": {
|
|
@@ -176,29 +187,49 @@ OpenCode 支持各种模型,我们这个插件也建议用各种前沿 LLM。
|
|
|
176
187
|
}
|
|
177
188
|
```
|
|
178
189
|
|
|
179
|
-
2. **有 ChatGPT 订阅吗?** 没有的话,oracle Agent 就用
|
|
190
|
+
2. **有 ChatGPT 订阅吗?** 没有的话,oracle Agent 就用 fallback 模型了。
|
|
180
191
|
- 用户说**有**,加个待办:"配置用户 ChatGPT 认证"
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"
|
|
192
|
+
- 用户说**没有**:
|
|
193
|
+
- **有 Claude 的情况下**:把这个合并到 `oh-my-opencode.json`:
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"agents": {
|
|
197
|
+
"oracle": { "model": "anthropic/claude-opus-4-5" }
|
|
198
|
+
}
|
|
186
199
|
}
|
|
187
|
-
|
|
188
|
-
|
|
200
|
+
```
|
|
201
|
+
- **没有 Claude 的情况下**:把这个合并到 `oh-my-opencode.json`:
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"agents": {
|
|
205
|
+
"oracle": { "model": "opencode/big-pickle" }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
189
209
|
|
|
190
210
|
3. **要接 Gemini 模型吗?** 不接的话,那么漂亮的 UI 设计你就无福消受了。
|
|
191
211
|
- 用户说**要**,加个待办:"配置 Google OAuth"
|
|
192
|
-
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
- 用户说**不要**:
|
|
213
|
+
- **有 Claude 的情况下**:把这个合并到 `oh-my-opencode.json`:
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"agents": {
|
|
217
|
+
"frontend-ui-ux-engineer": { "model": "anthropic/claude-opus-4-5" },
|
|
218
|
+
"document-writer": { "model": "anthropic/claude-opus-4-5" },
|
|
219
|
+
"multimodal-looker": { "model": "anthropic/claude-opus-4-5" }
|
|
220
|
+
}
|
|
199
221
|
}
|
|
200
|
-
|
|
201
|
-
|
|
222
|
+
```
|
|
223
|
+
- **没有 Claude 的情况下**:把这个合并到 `oh-my-opencode.json`:
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"agents": {
|
|
227
|
+
"frontend-ui-ux-engineer": { "model": "opencode/big-pickle" },
|
|
228
|
+
"document-writer": { "model": "opencode/big-pickle" },
|
|
229
|
+
"multimodal-looker": { "model": "opencode/big-pickle" }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
202
233
|
|
|
203
234
|
### 步骤 1:确认 OpenCode 装没装
|
|
204
235
|
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -1092,6 +1092,7 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
|
|
|
1092
1092
|
empty_message_recovery: z.ZodOptional<z.ZodBoolean>;
|
|
1093
1093
|
auto_resume: z.ZodOptional<z.ZodBoolean>;
|
|
1094
1094
|
}, z.core.$strip>>;
|
|
1095
|
+
auto_update: z.ZodOptional<z.ZodBoolean>;
|
|
1095
1096
|
}, z.core.$strip>;
|
|
1096
1097
|
export type OhMyOpenCodeConfig = z.infer<typeof OhMyOpenCodeConfigSchema>;
|
|
1097
1098
|
export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>;
|
|
@@ -6,8 +6,15 @@ export interface PluginEntryInfo {
|
|
|
6
6
|
entry: string;
|
|
7
7
|
isPinned: boolean;
|
|
8
8
|
pinnedVersion: string | null;
|
|
9
|
+
configPath: string;
|
|
9
10
|
}
|
|
10
11
|
export declare function findPluginEntry(directory: string): PluginEntryInfo | null;
|
|
11
12
|
export declare function getCachedVersion(): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Updates a pinned version entry in the config file.
|
|
15
|
+
* Only replaces within the "plugin" array to avoid unintended edits.
|
|
16
|
+
* Preserves JSONC comments and formatting via string replacement.
|
|
17
|
+
*/
|
|
18
|
+
export declare function updatePinnedVersion(configPath: string, oldEntry: string, newVersion: string): boolean;
|
|
12
19
|
export declare function getLatestVersion(): Promise<string | null>;
|
|
13
20
|
export declare function checkForUpdate(directory: string): Promise<UpdateCheckResult>;
|
|
@@ -6,7 +6,7 @@ export declare function createAutoUpdateCheckerHook(ctx: PluginInput, options?:
|
|
|
6
6
|
type: string;
|
|
7
7
|
properties?: unknown;
|
|
8
8
|
};
|
|
9
|
-
}) =>
|
|
9
|
+
}) => void;
|
|
10
10
|
};
|
|
11
11
|
export type { UpdateCheckResult, AutoUpdateCheckerOptions } from "./types";
|
|
12
12
|
export { checkForUpdate } from "./checker";
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export { createContextWindowMonitorHook } from "./context-window-monitor";
|
|
|
3
3
|
export { createSessionNotification } from "./session-notification";
|
|
4
4
|
export { createSessionRecoveryHook, type SessionRecoveryHook, type SessionRecoveryOptions } from "./session-recovery";
|
|
5
5
|
export { createCommentCheckerHooks } from "./comment-checker";
|
|
6
|
-
export { createGrepOutputTruncatorHook } from "./grep-output-truncator";
|
|
7
6
|
export { createToolOutputTruncatorHook } from "./tool-output-truncator";
|
|
8
7
|
export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
|
|
9
8
|
export { createDirectoryReadmeInjectorHook } from "./directory-readme-injector";
|
package/dist/index.js
CHANGED
|
@@ -3241,6 +3241,15 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
|
|
|
3241
3241
|
import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
|
|
3242
3242
|
import { join as join6 } from "path";
|
|
3243
3243
|
|
|
3244
|
+
// src/features/claude-code-session-state/state.ts
|
|
3245
|
+
var subagentSessions = new Set;
|
|
3246
|
+
var mainSessionID;
|
|
3247
|
+
function setMainSession(id) {
|
|
3248
|
+
mainSessionID = id;
|
|
3249
|
+
}
|
|
3250
|
+
function getMainSessionID() {
|
|
3251
|
+
return mainSessionID;
|
|
3252
|
+
}
|
|
3244
3253
|
// src/features/hook-message-injector/injector.ts
|
|
3245
3254
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
|
|
3246
3255
|
import { join as join5 } from "path";
|
|
@@ -3399,12 +3408,14 @@ function detectInterrupt(error) {
|
|
|
3399
3408
|
}
|
|
3400
3409
|
return false;
|
|
3401
3410
|
}
|
|
3411
|
+
var COUNTDOWN_SECONDS = 5;
|
|
3412
|
+
var TOAST_DURATION_MS = 900;
|
|
3402
3413
|
function createTodoContinuationEnforcer(ctx) {
|
|
3403
3414
|
const remindedSessions = new Set;
|
|
3404
3415
|
const interruptedSessions = new Set;
|
|
3405
3416
|
const errorSessions = new Set;
|
|
3406
3417
|
const recoveringSessions = new Set;
|
|
3407
|
-
const
|
|
3418
|
+
const pendingCountdowns = new Map;
|
|
3408
3419
|
const markRecovering = (sessionID) => {
|
|
3409
3420
|
recoveringSessions.add(sessionID);
|
|
3410
3421
|
};
|
|
@@ -3422,10 +3433,10 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3422
3433
|
interruptedSessions.add(sessionID);
|
|
3423
3434
|
}
|
|
3424
3435
|
log(`[${HOOK_NAME}] session.error received`, { sessionID, isInterrupt, error: props?.error });
|
|
3425
|
-
const
|
|
3426
|
-
if (
|
|
3427
|
-
|
|
3428
|
-
|
|
3436
|
+
const countdown = pendingCountdowns.get(sessionID);
|
|
3437
|
+
if (countdown) {
|
|
3438
|
+
clearInterval(countdown.intervalId);
|
|
3439
|
+
pendingCountdowns.delete(sessionID);
|
|
3429
3440
|
}
|
|
3430
3441
|
}
|
|
3431
3442
|
return;
|
|
@@ -3435,57 +3446,78 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3435
3446
|
if (!sessionID)
|
|
3436
3447
|
return;
|
|
3437
3448
|
log(`[${HOOK_NAME}] session.idle received`, { sessionID });
|
|
3438
|
-
const
|
|
3439
|
-
if (
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
}
|
|
3443
|
-
const
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3449
|
+
const mainSessionID2 = getMainSessionID();
|
|
3450
|
+
if (mainSessionID2 && sessionID !== mainSessionID2) {
|
|
3451
|
+
log(`[${HOOK_NAME}] Skipped: not main session`, { sessionID, mainSessionID: mainSessionID2 });
|
|
3452
|
+
return;
|
|
3453
|
+
}
|
|
3454
|
+
const existingCountdown = pendingCountdowns.get(sessionID);
|
|
3455
|
+
if (existingCountdown) {
|
|
3456
|
+
clearInterval(existingCountdown.intervalId);
|
|
3457
|
+
pendingCountdowns.delete(sessionID);
|
|
3458
|
+
log(`[${HOOK_NAME}] Cancelled existing countdown`, { sessionID });
|
|
3459
|
+
}
|
|
3460
|
+
if (recoveringSessions.has(sessionID)) {
|
|
3461
|
+
log(`[${HOOK_NAME}] Skipped: session in recovery mode`, { sessionID });
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
const shouldBypass = interruptedSessions.has(sessionID) || errorSessions.has(sessionID);
|
|
3465
|
+
if (shouldBypass) {
|
|
3451
3466
|
interruptedSessions.delete(sessionID);
|
|
3452
3467
|
errorSessions.delete(sessionID);
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
}
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3468
|
+
log(`[${HOOK_NAME}] Skipped: error/interrupt bypass`, { sessionID });
|
|
3469
|
+
return;
|
|
3470
|
+
}
|
|
3471
|
+
if (remindedSessions.has(sessionID)) {
|
|
3472
|
+
log(`[${HOOK_NAME}] Skipped: already reminded this session`, { sessionID });
|
|
3473
|
+
return;
|
|
3474
|
+
}
|
|
3475
|
+
let todos = [];
|
|
3476
|
+
try {
|
|
3477
|
+
log(`[${HOOK_NAME}] Fetching todos for session`, { sessionID });
|
|
3478
|
+
const response = await ctx.client.session.todo({
|
|
3479
|
+
path: { id: sessionID }
|
|
3480
|
+
});
|
|
3481
|
+
todos = response.data ?? response;
|
|
3482
|
+
log(`[${HOOK_NAME}] Todo API response`, { sessionID, todosCount: todos?.length ?? 0 });
|
|
3483
|
+
} catch (err) {
|
|
3484
|
+
log(`[${HOOK_NAME}] Todo API error`, { sessionID, error: String(err) });
|
|
3485
|
+
return;
|
|
3486
|
+
}
|
|
3487
|
+
if (!todos || todos.length === 0) {
|
|
3488
|
+
log(`[${HOOK_NAME}] No todos found`, { sessionID });
|
|
3489
|
+
return;
|
|
3490
|
+
}
|
|
3491
|
+
const incomplete = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
|
|
3492
|
+
if (incomplete.length === 0) {
|
|
3493
|
+
log(`[${HOOK_NAME}] All todos completed`, { sessionID, total: todos.length });
|
|
3494
|
+
return;
|
|
3495
|
+
}
|
|
3496
|
+
log(`[${HOOK_NAME}] Found incomplete todos, starting countdown`, { sessionID, incomplete: incomplete.length, total: todos.length });
|
|
3497
|
+
const showCountdownToast = async (seconds) => {
|
|
3498
|
+
await ctx.client.tui.showToast({
|
|
3499
|
+
body: {
|
|
3500
|
+
title: "Todo Continuation",
|
|
3501
|
+
message: `Resuming in ${seconds}s... (${incomplete.length} tasks remaining)`,
|
|
3502
|
+
variant: "warning",
|
|
3503
|
+
duration: TOAST_DURATION_MS
|
|
3504
|
+
}
|
|
3505
|
+
}).catch(() => {});
|
|
3506
|
+
};
|
|
3507
|
+
const executeAfterCountdown = async () => {
|
|
3508
|
+
pendingCountdowns.delete(sessionID);
|
|
3509
|
+
log(`[${HOOK_NAME}] Countdown finished, executing continuation`, { sessionID });
|
|
3510
|
+
if (recoveringSessions.has(sessionID)) {
|
|
3511
|
+
log(`[${HOOK_NAME}] Abort: session entered recovery mode during countdown`, { sessionID });
|
|
3475
3512
|
return;
|
|
3476
3513
|
}
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3514
|
+
if (interruptedSessions.has(sessionID) || errorSessions.has(sessionID)) {
|
|
3515
|
+
log(`[${HOOK_NAME}] Abort: error/interrupt occurred during countdown`, { sessionID });
|
|
3516
|
+
interruptedSessions.delete(sessionID);
|
|
3517
|
+
errorSessions.delete(sessionID);
|
|
3480
3518
|
return;
|
|
3481
3519
|
}
|
|
3482
|
-
log(`[${HOOK_NAME}] Found incomplete todos`, { sessionID, incomplete: incomplete.length, total: todos.length });
|
|
3483
3520
|
remindedSessions.add(sessionID);
|
|
3484
|
-
if (interruptedSessions.has(sessionID) || errorSessions.has(sessionID) || recoveringSessions.has(sessionID)) {
|
|
3485
|
-
log(`[${HOOK_NAME}] Abort occurred during delay/fetch`, { sessionID });
|
|
3486
|
-
remindedSessions.delete(sessionID);
|
|
3487
|
-
return;
|
|
3488
|
-
}
|
|
3489
3521
|
try {
|
|
3490
3522
|
const messageDir = getMessageDir(sessionID);
|
|
3491
3523
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
@@ -3516,19 +3548,37 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3516
3548
|
log(`[${HOOK_NAME}] Prompt injection failed`, { sessionID, error: String(err) });
|
|
3517
3549
|
remindedSessions.delete(sessionID);
|
|
3518
3550
|
}
|
|
3519
|
-
}
|
|
3520
|
-
|
|
3551
|
+
};
|
|
3552
|
+
let secondsRemaining = COUNTDOWN_SECONDS;
|
|
3553
|
+
showCountdownToast(secondsRemaining).catch(() => {});
|
|
3554
|
+
const intervalId = setInterval(() => {
|
|
3555
|
+
secondsRemaining--;
|
|
3556
|
+
if (secondsRemaining <= 0) {
|
|
3557
|
+
clearInterval(intervalId);
|
|
3558
|
+
pendingCountdowns.delete(sessionID);
|
|
3559
|
+
executeAfterCountdown();
|
|
3560
|
+
return;
|
|
3561
|
+
}
|
|
3562
|
+
const countdown = pendingCountdowns.get(sessionID);
|
|
3563
|
+
if (!countdown) {
|
|
3564
|
+
clearInterval(intervalId);
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
countdown.secondsRemaining = secondsRemaining;
|
|
3568
|
+
showCountdownToast(secondsRemaining).catch(() => {});
|
|
3569
|
+
}, 1000);
|
|
3570
|
+
pendingCountdowns.set(sessionID, { secondsRemaining, intervalId });
|
|
3521
3571
|
}
|
|
3522
3572
|
if (event.type === "message.updated") {
|
|
3523
3573
|
const info = props?.info;
|
|
3524
3574
|
const sessionID = info?.sessionID;
|
|
3525
3575
|
log(`[${HOOK_NAME}] message.updated received`, { sessionID, role: info?.role });
|
|
3526
3576
|
if (sessionID && info?.role === "user") {
|
|
3527
|
-
const
|
|
3528
|
-
if (
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
log(`[${HOOK_NAME}] Cancelled
|
|
3577
|
+
const countdown = pendingCountdowns.get(sessionID);
|
|
3578
|
+
if (countdown) {
|
|
3579
|
+
clearInterval(countdown.intervalId);
|
|
3580
|
+
pendingCountdowns.delete(sessionID);
|
|
3581
|
+
log(`[${HOOK_NAME}] Cancelled countdown on user message`, { sessionID });
|
|
3532
3582
|
}
|
|
3533
3583
|
}
|
|
3534
3584
|
if (sessionID && info?.role === "assistant" && remindedSessions.has(sessionID)) {
|
|
@@ -3543,10 +3593,10 @@ function createTodoContinuationEnforcer(ctx) {
|
|
|
3543
3593
|
interruptedSessions.delete(sessionInfo.id);
|
|
3544
3594
|
errorSessions.delete(sessionInfo.id);
|
|
3545
3595
|
recoveringSessions.delete(sessionInfo.id);
|
|
3546
|
-
const
|
|
3547
|
-
if (
|
|
3548
|
-
|
|
3549
|
-
|
|
3596
|
+
const countdown = pendingCountdowns.get(sessionInfo.id);
|
|
3597
|
+
if (countdown) {
|
|
3598
|
+
clearInterval(countdown.intervalId);
|
|
3599
|
+
pendingCountdowns.delete(sessionInfo.id);
|
|
3550
3600
|
}
|
|
3551
3601
|
}
|
|
3552
3602
|
}
|
|
@@ -3616,17 +3666,6 @@ ${CONTEXT_REMINDER}
|
|
|
3616
3666
|
}
|
|
3617
3667
|
// src/hooks/session-notification.ts
|
|
3618
3668
|
import { platform } from "os";
|
|
3619
|
-
|
|
3620
|
-
// src/features/claude-code-session-state/state.ts
|
|
3621
|
-
var subagentSessions = new Set;
|
|
3622
|
-
var mainSessionID;
|
|
3623
|
-
function setMainSession(id) {
|
|
3624
|
-
mainSessionID = id;
|
|
3625
|
-
}
|
|
3626
|
-
function getMainSessionID() {
|
|
3627
|
-
return mainSessionID;
|
|
3628
|
-
}
|
|
3629
|
-
// src/hooks/session-notification.ts
|
|
3630
3669
|
function detectPlatform() {
|
|
3631
3670
|
const p = platform();
|
|
3632
3671
|
if (p === "darwin" || p === "linux" || p === "win32")
|
|
@@ -4712,6 +4751,8 @@ ${result.message}`;
|
|
|
4712
4751
|
}
|
|
4713
4752
|
// src/hooks/tool-output-truncator.ts
|
|
4714
4753
|
var TRUNCATABLE_TOOLS = [
|
|
4754
|
+
"grep",
|
|
4755
|
+
"Grep",
|
|
4715
4756
|
"safe_grep",
|
|
4716
4757
|
"glob",
|
|
4717
4758
|
"Glob",
|
|
@@ -5708,12 +5749,16 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
|
|
|
5708
5749
|
});
|
|
5709
5750
|
fallbackState.revertAttempt++;
|
|
5710
5751
|
fallbackState.lastRevertedMessageID = pair.userMessageID;
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5752
|
+
clearSessionState(autoCompactState, sessionID);
|
|
5753
|
+
setTimeout(async () => {
|
|
5754
|
+
try {
|
|
5755
|
+
await client.session.prompt_async({
|
|
5756
|
+
path: { sessionID },
|
|
5757
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
5758
|
+
query: { directory }
|
|
5759
|
+
});
|
|
5760
|
+
} catch {}
|
|
5761
|
+
}, 500);
|
|
5717
5762
|
return;
|
|
5718
5763
|
} catch {}
|
|
5719
5764
|
} else {
|
|
@@ -7502,9 +7547,6 @@ var USER_OPENCODE_CONFIG = path4.join(USER_CONFIG_DIR, "opencode", "opencode.jso
|
|
|
7502
7547
|
var USER_OPENCODE_CONFIG_JSONC = path4.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
|
|
7503
7548
|
|
|
7504
7549
|
// src/hooks/auto-update-checker/checker.ts
|
|
7505
|
-
function isLocalDevMode(directory) {
|
|
7506
|
-
return getLocalDevPath(directory) !== null;
|
|
7507
|
-
}
|
|
7508
7550
|
function stripJsonComments(json) {
|
|
7509
7551
|
return json.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m).replace(/,(\s*[}\]])/g, "$1");
|
|
7510
7552
|
}
|
|
@@ -7586,12 +7628,12 @@ function findPluginEntry(directory) {
|
|
|
7586
7628
|
const plugins = config.plugin ?? [];
|
|
7587
7629
|
for (const entry of plugins) {
|
|
7588
7630
|
if (entry === PACKAGE_NAME) {
|
|
7589
|
-
return { entry, isPinned: false, pinnedVersion: null };
|
|
7631
|
+
return { entry, isPinned: false, pinnedVersion: null, configPath };
|
|
7590
7632
|
}
|
|
7591
7633
|
if (entry.startsWith(`${PACKAGE_NAME}@`)) {
|
|
7592
7634
|
const pinnedVersion = entry.slice(PACKAGE_NAME.length + 1);
|
|
7593
7635
|
const isPinned = pinnedVersion !== "latest";
|
|
7594
|
-
return { entry, isPinned, pinnedVersion: isPinned ? pinnedVersion : null };
|
|
7636
|
+
return { entry, isPinned, pinnedVersion: isPinned ? pinnedVersion : null, configPath };
|
|
7595
7637
|
}
|
|
7596
7638
|
}
|
|
7597
7639
|
} catch {
|
|
@@ -7623,6 +7665,48 @@ function getCachedVersion() {
|
|
|
7623
7665
|
}
|
|
7624
7666
|
return null;
|
|
7625
7667
|
}
|
|
7668
|
+
function updatePinnedVersion(configPath, oldEntry, newVersion) {
|
|
7669
|
+
try {
|
|
7670
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
7671
|
+
const newEntry = `${PACKAGE_NAME}@${newVersion}`;
|
|
7672
|
+
const pluginMatch = content.match(/"plugin"\s*:\s*\[/);
|
|
7673
|
+
if (!pluginMatch || pluginMatch.index === undefined) {
|
|
7674
|
+
log(`[auto-update-checker] No "plugin" array found in ${configPath}`);
|
|
7675
|
+
return false;
|
|
7676
|
+
}
|
|
7677
|
+
const startIdx = pluginMatch.index + pluginMatch[0].length;
|
|
7678
|
+
let bracketCount = 1;
|
|
7679
|
+
let endIdx = startIdx;
|
|
7680
|
+
for (let i = startIdx;i < content.length && bracketCount > 0; i++) {
|
|
7681
|
+
if (content[i] === "[")
|
|
7682
|
+
bracketCount++;
|
|
7683
|
+
else if (content[i] === "]")
|
|
7684
|
+
bracketCount--;
|
|
7685
|
+
endIdx = i;
|
|
7686
|
+
}
|
|
7687
|
+
const before = content.slice(0, startIdx);
|
|
7688
|
+
const pluginArrayContent = content.slice(startIdx, endIdx);
|
|
7689
|
+
const after = content.slice(endIdx);
|
|
7690
|
+
const escapedOldEntry = oldEntry.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7691
|
+
const regex = new RegExp(`["']${escapedOldEntry}["']`);
|
|
7692
|
+
if (!regex.test(pluginArrayContent)) {
|
|
7693
|
+
log(`[auto-update-checker] Entry "${oldEntry}" not found in plugin array of ${configPath}`);
|
|
7694
|
+
return false;
|
|
7695
|
+
}
|
|
7696
|
+
const updatedPluginArray = pluginArrayContent.replace(regex, `"${newEntry}"`);
|
|
7697
|
+
const updatedContent = before + updatedPluginArray + after;
|
|
7698
|
+
if (updatedContent === content) {
|
|
7699
|
+
log(`[auto-update-checker] No changes made to ${configPath}`);
|
|
7700
|
+
return false;
|
|
7701
|
+
}
|
|
7702
|
+
fs4.writeFileSync(configPath, updatedContent, "utf-8");
|
|
7703
|
+
log(`[auto-update-checker] Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`);
|
|
7704
|
+
return true;
|
|
7705
|
+
} catch (err) {
|
|
7706
|
+
log(`[auto-update-checker] Failed to update config file ${configPath}:`, err);
|
|
7707
|
+
return false;
|
|
7708
|
+
}
|
|
7709
|
+
}
|
|
7626
7710
|
async function getLatestVersion() {
|
|
7627
7711
|
const controller = new AbortController;
|
|
7628
7712
|
const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
|
|
@@ -7641,44 +7725,45 @@ async function getLatestVersion() {
|
|
|
7641
7725
|
clearTimeout(timeoutId);
|
|
7642
7726
|
}
|
|
7643
7727
|
}
|
|
7644
|
-
async function checkForUpdate(directory) {
|
|
7645
|
-
if (isLocalDevMode(directory)) {
|
|
7646
|
-
log("[auto-update-checker] Local dev mode detected, skipping update check");
|
|
7647
|
-
return { needsUpdate: false, currentVersion: null, latestVersion: null, isLocalDev: true, isPinned: false };
|
|
7648
|
-
}
|
|
7649
|
-
const pluginInfo = findPluginEntry(directory);
|
|
7650
|
-
if (!pluginInfo) {
|
|
7651
|
-
log("[auto-update-checker] Plugin not found in config");
|
|
7652
|
-
return { needsUpdate: false, currentVersion: null, latestVersion: null, isLocalDev: false, isPinned: false };
|
|
7653
|
-
}
|
|
7654
|
-
if (pluginInfo.isPinned) {
|
|
7655
|
-
log(`[auto-update-checker] Version pinned to ${pluginInfo.pinnedVersion}, skipping update check`);
|
|
7656
|
-
return { needsUpdate: false, currentVersion: pluginInfo.pinnedVersion, latestVersion: null, isLocalDev: false, isPinned: true };
|
|
7657
|
-
}
|
|
7658
|
-
const currentVersion = getCachedVersion();
|
|
7659
|
-
if (!currentVersion) {
|
|
7660
|
-
log("[auto-update-checker] No cached version found");
|
|
7661
|
-
return { needsUpdate: false, currentVersion: null, latestVersion: null, isLocalDev: false, isPinned: false };
|
|
7662
|
-
}
|
|
7663
|
-
const latestVersion = await getLatestVersion();
|
|
7664
|
-
if (!latestVersion) {
|
|
7665
|
-
log("[auto-update-checker] Failed to fetch latest version");
|
|
7666
|
-
return { needsUpdate: false, currentVersion, latestVersion: null, isLocalDev: false, isPinned: false };
|
|
7667
|
-
}
|
|
7668
|
-
const needsUpdate = currentVersion !== latestVersion;
|
|
7669
|
-
log(`[auto-update-checker] Current: ${currentVersion}, Latest: ${latestVersion}, NeedsUpdate: ${needsUpdate}`);
|
|
7670
|
-
return { needsUpdate, currentVersion, latestVersion, isLocalDev: false, isPinned: false };
|
|
7671
|
-
}
|
|
7672
7728
|
|
|
7673
7729
|
// src/hooks/auto-update-checker/cache.ts
|
|
7674
7730
|
import * as fs5 from "fs";
|
|
7675
7731
|
import * as path6 from "path";
|
|
7732
|
+
function stripTrailingCommas(json) {
|
|
7733
|
+
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
7734
|
+
}
|
|
7735
|
+
function removeFromBunLock(packageName) {
|
|
7736
|
+
const lockPath = path6.join(CACHE_DIR, "bun.lock");
|
|
7737
|
+
if (!fs5.existsSync(lockPath))
|
|
7738
|
+
return false;
|
|
7739
|
+
try {
|
|
7740
|
+
const content = fs5.readFileSync(lockPath, "utf-8");
|
|
7741
|
+
const lock = JSON.parse(stripTrailingCommas(content));
|
|
7742
|
+
let modified = false;
|
|
7743
|
+
if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
|
|
7744
|
+
delete lock.workspaces[""].dependencies[packageName];
|
|
7745
|
+
modified = true;
|
|
7746
|
+
}
|
|
7747
|
+
if (lock.packages?.[packageName]) {
|
|
7748
|
+
delete lock.packages[packageName];
|
|
7749
|
+
modified = true;
|
|
7750
|
+
}
|
|
7751
|
+
if (modified) {
|
|
7752
|
+
fs5.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
|
|
7753
|
+
log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
|
|
7754
|
+
}
|
|
7755
|
+
return modified;
|
|
7756
|
+
} catch {
|
|
7757
|
+
return false;
|
|
7758
|
+
}
|
|
7759
|
+
}
|
|
7676
7760
|
function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
7677
7761
|
try {
|
|
7678
7762
|
const pkgDir = path6.join(CACHE_DIR, "node_modules", packageName);
|
|
7679
7763
|
const pkgJsonPath = path6.join(CACHE_DIR, "package.json");
|
|
7680
7764
|
let packageRemoved = false;
|
|
7681
7765
|
let dependencyRemoved = false;
|
|
7766
|
+
let lockRemoved = false;
|
|
7682
7767
|
if (fs5.existsSync(pkgDir)) {
|
|
7683
7768
|
fs5.rmSync(pkgDir, { recursive: true, force: true });
|
|
7684
7769
|
log(`[auto-update-checker] Package removed: ${pkgDir}`);
|
|
@@ -7694,7 +7779,8 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
|
7694
7779
|
dependencyRemoved = true;
|
|
7695
7780
|
}
|
|
7696
7781
|
}
|
|
7697
|
-
|
|
7782
|
+
lockRemoved = removeFromBunLock(packageName);
|
|
7783
|
+
if (!packageRemoved && !dependencyRemoved && !lockRemoved) {
|
|
7698
7784
|
log(`[auto-update-checker] Package not found, nothing to invalidate: ${packageName}`);
|
|
7699
7785
|
return false;
|
|
7700
7786
|
}
|
|
@@ -7707,7 +7793,7 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
|
7707
7793
|
|
|
7708
7794
|
// src/hooks/auto-update-checker/index.ts
|
|
7709
7795
|
function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
7710
|
-
const { showStartupToast = true, isSisyphusEnabled = false } = options;
|
|
7796
|
+
const { showStartupToast = true, isSisyphusEnabled = false, autoUpdate = true } = options;
|
|
7711
7797
|
const getToastMessage = (isUpdate, latestVersion) => {
|
|
7712
7798
|
if (isSisyphusEnabled) {
|
|
7713
7799
|
return isUpdate ? `Sisyphus on steroids is steering OpenCode.
|
|
@@ -7716,21 +7802,9 @@ v${latestVersion} available. Restart to apply.` : `Sisyphus on steroids is steer
|
|
|
7716
7802
|
return isUpdate ? `OpenCode is now on Steroids. oMoMoMoMo...
|
|
7717
7803
|
v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on Steroids. oMoMoMoMo...`;
|
|
7718
7804
|
};
|
|
7719
|
-
const showVersionToast = async (version) => {
|
|
7720
|
-
const displayVersion = version ?? "unknown";
|
|
7721
|
-
await ctx.client.tui.showToast({
|
|
7722
|
-
body: {
|
|
7723
|
-
title: `OhMyOpenCode ${displayVersion}`,
|
|
7724
|
-
message: getToastMessage(false),
|
|
7725
|
-
variant: "info",
|
|
7726
|
-
duration: 5000
|
|
7727
|
-
}
|
|
7728
|
-
}).catch(() => {});
|
|
7729
|
-
log(`[auto-update-checker] Startup toast shown: v${displayVersion}`);
|
|
7730
|
-
};
|
|
7731
7805
|
let hasChecked = false;
|
|
7732
7806
|
return {
|
|
7733
|
-
event:
|
|
7807
|
+
event: ({ event }) => {
|
|
7734
7808
|
if (event.type !== "session.created")
|
|
7735
7809
|
return;
|
|
7736
7810
|
if (hasChecked)
|
|
@@ -7739,47 +7813,69 @@ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on S
|
|
|
7739
7813
|
if (props?.info?.parentID)
|
|
7740
7814
|
return;
|
|
7741
7815
|
hasChecked = true;
|
|
7742
|
-
|
|
7743
|
-
const
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
await showVersionToast(version);
|
|
7749
|
-
}
|
|
7750
|
-
return;
|
|
7751
|
-
}
|
|
7752
|
-
if (result.isPinned) {
|
|
7753
|
-
log(`[auto-update-checker] Skipped: version pinned to ${result.currentVersion}`);
|
|
7816
|
+
setTimeout(() => {
|
|
7817
|
+
const cachedVersion = getCachedVersion();
|
|
7818
|
+
const localDevVersion = getLocalDevVersion(ctx.directory);
|
|
7819
|
+
const displayVersion = localDevVersion ?? cachedVersion;
|
|
7820
|
+
showConfigErrorsIfAny(ctx).catch(() => {});
|
|
7821
|
+
if (localDevVersion) {
|
|
7754
7822
|
if (showStartupToast) {
|
|
7755
|
-
|
|
7823
|
+
showLocalDevToast(ctx, displayVersion, isSisyphusEnabled).catch(() => {});
|
|
7756
7824
|
}
|
|
7825
|
+
log("[auto-update-checker] Local development mode");
|
|
7757
7826
|
return;
|
|
7758
7827
|
}
|
|
7759
|
-
if (
|
|
7760
|
-
|
|
7761
|
-
if (showStartupToast) {
|
|
7762
|
-
await showVersionToast(result.currentVersion);
|
|
7763
|
-
}
|
|
7764
|
-
return;
|
|
7828
|
+
if (showStartupToast) {
|
|
7829
|
+
showVersionToast(ctx, displayVersion, getToastMessage(false)).catch(() => {});
|
|
7765
7830
|
}
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
message: getToastMessage(true, result.latestVersion ?? undefined),
|
|
7771
|
-
variant: "info",
|
|
7772
|
-
duration: 8000
|
|
7773
|
-
}
|
|
7774
|
-
}).catch(() => {});
|
|
7775
|
-
log(`[auto-update-checker] Update notification sent: v${result.currentVersion} \u2192 v${result.latestVersion}`);
|
|
7776
|
-
} catch (err) {
|
|
7777
|
-
log("[auto-update-checker] Error during update check:", err);
|
|
7778
|
-
}
|
|
7779
|
-
await showConfigErrorsIfAny(ctx);
|
|
7831
|
+
runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage).catch((err) => {
|
|
7832
|
+
log("[auto-update-checker] Background update check failed:", err);
|
|
7833
|
+
});
|
|
7834
|
+
}, 0);
|
|
7780
7835
|
}
|
|
7781
7836
|
};
|
|
7782
7837
|
}
|
|
7838
|
+
async function runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage) {
|
|
7839
|
+
const pluginInfo = findPluginEntry(ctx.directory);
|
|
7840
|
+
if (!pluginInfo) {
|
|
7841
|
+
log("[auto-update-checker] Plugin not found in config");
|
|
7842
|
+
return;
|
|
7843
|
+
}
|
|
7844
|
+
const cachedVersion = getCachedVersion();
|
|
7845
|
+
const currentVersion = cachedVersion ?? pluginInfo.pinnedVersion;
|
|
7846
|
+
if (!currentVersion) {
|
|
7847
|
+
log("[auto-update-checker] No version found (cached or pinned)");
|
|
7848
|
+
return;
|
|
7849
|
+
}
|
|
7850
|
+
const latestVersion = await getLatestVersion();
|
|
7851
|
+
if (!latestVersion) {
|
|
7852
|
+
log("[auto-update-checker] Failed to fetch latest version");
|
|
7853
|
+
return;
|
|
7854
|
+
}
|
|
7855
|
+
if (currentVersion === latestVersion) {
|
|
7856
|
+
log("[auto-update-checker] Already on latest version");
|
|
7857
|
+
return;
|
|
7858
|
+
}
|
|
7859
|
+
log(`[auto-update-checker] Update available: ${currentVersion} \u2192 ${latestVersion}`);
|
|
7860
|
+
if (!autoUpdate) {
|
|
7861
|
+
await showUpdateAvailableToast(ctx, latestVersion, getToastMessage);
|
|
7862
|
+
log("[auto-update-checker] Auto-update disabled, notification only");
|
|
7863
|
+
return;
|
|
7864
|
+
}
|
|
7865
|
+
if (pluginInfo.isPinned) {
|
|
7866
|
+
const updated = updatePinnedVersion(pluginInfo.configPath, pluginInfo.entry, latestVersion);
|
|
7867
|
+
if (updated) {
|
|
7868
|
+
invalidatePackage(PACKAGE_NAME);
|
|
7869
|
+
await showAutoUpdatedToast(ctx, currentVersion, latestVersion);
|
|
7870
|
+
log(`[auto-update-checker] Config updated: ${pluginInfo.entry} \u2192 ${PACKAGE_NAME}@${latestVersion}`);
|
|
7871
|
+
} else {
|
|
7872
|
+
await showUpdateAvailableToast(ctx, latestVersion, getToastMessage);
|
|
7873
|
+
}
|
|
7874
|
+
} else {
|
|
7875
|
+
invalidatePackage(PACKAGE_NAME);
|
|
7876
|
+
await showUpdateAvailableToast(ctx, latestVersion, getToastMessage);
|
|
7877
|
+
}
|
|
7878
|
+
}
|
|
7783
7879
|
async function showConfigErrorsIfAny(ctx) {
|
|
7784
7880
|
const errors = getConfigLoadErrors();
|
|
7785
7881
|
if (errors.length === 0)
|
|
@@ -7798,12 +7894,60 @@ ${errorMessages}`,
|
|
|
7798
7894
|
log(`[auto-update-checker] Config load errors shown: ${errors.length} error(s)`);
|
|
7799
7895
|
clearConfigLoadErrors();
|
|
7800
7896
|
}
|
|
7897
|
+
async function showVersionToast(ctx, version, message) {
|
|
7898
|
+
const displayVersion = version ?? "unknown";
|
|
7899
|
+
await ctx.client.tui.showToast({
|
|
7900
|
+
body: {
|
|
7901
|
+
title: `OhMyOpenCode ${displayVersion}`,
|
|
7902
|
+
message,
|
|
7903
|
+
variant: "info",
|
|
7904
|
+
duration: 5000
|
|
7905
|
+
}
|
|
7906
|
+
}).catch(() => {});
|
|
7907
|
+
log(`[auto-update-checker] Startup toast shown: v${displayVersion}`);
|
|
7908
|
+
}
|
|
7909
|
+
async function showUpdateAvailableToast(ctx, latestVersion, getToastMessage) {
|
|
7910
|
+
await ctx.client.tui.showToast({
|
|
7911
|
+
body: {
|
|
7912
|
+
title: `OhMyOpenCode ${latestVersion}`,
|
|
7913
|
+
message: getToastMessage(true, latestVersion),
|
|
7914
|
+
variant: "info",
|
|
7915
|
+
duration: 8000
|
|
7916
|
+
}
|
|
7917
|
+
}).catch(() => {});
|
|
7918
|
+
log(`[auto-update-checker] Update available toast shown: v${latestVersion}`);
|
|
7919
|
+
}
|
|
7920
|
+
async function showAutoUpdatedToast(ctx, oldVersion, newVersion) {
|
|
7921
|
+
await ctx.client.tui.showToast({
|
|
7922
|
+
body: {
|
|
7923
|
+
title: `OhMyOpenCode Updated!`,
|
|
7924
|
+
message: `v${oldVersion} \u2192 v${newVersion}
|
|
7925
|
+
Restart OpenCode to apply.`,
|
|
7926
|
+
variant: "success",
|
|
7927
|
+
duration: 8000
|
|
7928
|
+
}
|
|
7929
|
+
}).catch(() => {});
|
|
7930
|
+
log(`[auto-update-checker] Auto-updated toast shown: v${oldVersion} \u2192 v${newVersion}`);
|
|
7931
|
+
}
|
|
7932
|
+
async function showLocalDevToast(ctx, version, isSisyphusEnabled) {
|
|
7933
|
+
const displayVersion = version ?? "dev";
|
|
7934
|
+
const message = isSisyphusEnabled ? "Sisyphus running in local development mode." : "Running in local development mode. oMoMoMo...";
|
|
7935
|
+
await ctx.client.tui.showToast({
|
|
7936
|
+
body: {
|
|
7937
|
+
title: `OhMyOpenCode ${displayVersion} (dev)`,
|
|
7938
|
+
message,
|
|
7939
|
+
variant: "warning",
|
|
7940
|
+
duration: 5000
|
|
7941
|
+
}
|
|
7942
|
+
}).catch(() => {});
|
|
7943
|
+
log(`[auto-update-checker] Local dev toast shown: v${displayVersion}`);
|
|
7944
|
+
}
|
|
7801
7945
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
7802
7946
|
import {
|
|
7803
7947
|
existsSync as existsSync21,
|
|
7804
7948
|
mkdirSync as mkdirSync8,
|
|
7805
7949
|
readFileSync as readFileSync13,
|
|
7806
|
-
writeFileSync as
|
|
7950
|
+
writeFileSync as writeFileSync10,
|
|
7807
7951
|
unlinkSync as unlinkSync7
|
|
7808
7952
|
} from "fs";
|
|
7809
7953
|
import { join as join30 } from "path";
|
|
@@ -7874,7 +8018,7 @@ function saveAgentUsageState(state2) {
|
|
|
7874
8018
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
7875
8019
|
}
|
|
7876
8020
|
const filePath = getStoragePath4(state2.sessionID);
|
|
7877
|
-
|
|
8021
|
+
writeFileSync10(filePath, JSON.stringify(state2, null, 2));
|
|
7878
8022
|
}
|
|
7879
8023
|
function clearAgentUsageState(sessionID) {
|
|
7880
8024
|
const filePath = getStoragePath4(sessionID);
|
|
@@ -7980,6 +8124,14 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
|
7980
8124
|
3. Always Use Plan agent with gathered context to create detailed work breakdown
|
|
7981
8125
|
4. Execute with continuous verification against original requirements
|
|
7982
8126
|
|
|
8127
|
+
## ZERO TOLERANCE FAILURES
|
|
8128
|
+
- **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation
|
|
8129
|
+
- **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100%
|
|
8130
|
+
- **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later"
|
|
8131
|
+
- **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified
|
|
8132
|
+
|
|
8133
|
+
THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT.
|
|
8134
|
+
|
|
7983
8135
|
</ultrawork-mode>
|
|
7984
8136
|
|
|
7985
8137
|
---
|
|
@@ -7998,18 +8150,17 @@ NEVER stop at first result - be exhaustive.`
|
|
|
7998
8150
|
{
|
|
7999
8151
|
pattern: /\b(analyze|analyse|investigate|examine|research|study|deep[\s-]?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to|\uBD84\uC11D|\uC870\uC0AC|\uD30C\uC545|\uC5F0\uAD6C|\uAC80\uD1A0|\uC9C4\uB2E8|\uC774\uD574|\uC124\uBA85|\uC6D0\uC778|\uC774\uC720|\uB72F\uC5B4\uBD10|\uB530\uC838\uBD10|\uD3C9\uAC00|\uD574\uC11D|\uB514\uBC84\uAE45|\uB514\uBC84\uADF8|\uC5B4\uB5BB\uAC8C|\uC65C|\uC0B4\uD3B4|\u5206\u6790|\u8ABF\u67FB|\u89E3\u6790|\u691C\u8A0E|\u7814\u7A76|\u8A3A\u65AD|\u7406\u89E3|\u8AAC\u660E|\u691C\u8A3C|\u7CBE\u67FB|\u7A76\u660E|\u30C7\u30D0\u30C3\u30B0|\u306A\u305C|\u3069\u3046|\u4ED5\u7D44\u307F|\u8C03\u67E5|\u68C0\u67E5|\u5256\u6790|\u6DF1\u5165|\u8BCA\u65AD|\u89E3\u91CA|\u8C03\u8BD5|\u4E3A\u4EC0\u4E48|\u539F\u7406|\u641E\u6E05\u695A|\u5F04\u660E\u767D|ph\u00E2n t\u00EDch|\u0111i\u1EC1u tra|nghi\u00EAn c\u1EE9u|ki\u1EC3m tra|xem x\u00E9t|ch\u1EA9n \u0111o\u00E1n|gi\u1EA3i th\u00EDch|t\u00ECm hi\u1EC3u|g\u1EE1 l\u1ED7i|t\u1EA1i sao/i,
|
|
8000
8152
|
message: `[analyze-mode]
|
|
8001
|
-
|
|
8153
|
+
ANALYSIS MODE. Gather context before diving deep:
|
|
8002
8154
|
|
|
8003
|
-
|
|
8004
|
-
-
|
|
8005
|
-
-
|
|
8006
|
-
-
|
|
8155
|
+
CONTEXT GATHERING (parallel):
|
|
8156
|
+
- 1-2 explore agents (codebase patterns, implementations)
|
|
8157
|
+
- 1-2 librarian agents (if external library involved)
|
|
8158
|
+
- Direct tools: Grep, AST-grep, LSP for targeted searches
|
|
8007
8159
|
|
|
8008
|
-
|
|
8009
|
-
-
|
|
8010
|
-
- Each oracle: different angle (architecture, performance, edge cases)
|
|
8160
|
+
IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
|
|
8161
|
+
- Consult oracle for strategic guidance
|
|
8011
8162
|
|
|
8012
|
-
SYNTHESIZE
|
|
8163
|
+
SYNTHESIZE findings before proceeding.`
|
|
8013
8164
|
}
|
|
8014
8165
|
];
|
|
8015
8166
|
|
|
@@ -8026,9 +8177,16 @@ function extractPromptText2(parts) {
|
|
|
8026
8177
|
}
|
|
8027
8178
|
|
|
8028
8179
|
// src/hooks/keyword-detector/index.ts
|
|
8180
|
+
var sessionFirstMessageProcessed2 = new Set;
|
|
8029
8181
|
function createKeywordDetectorHook() {
|
|
8030
8182
|
return {
|
|
8031
8183
|
"chat.message": async (input, output) => {
|
|
8184
|
+
const isFirstMessage = !sessionFirstMessageProcessed2.has(input.sessionID);
|
|
8185
|
+
sessionFirstMessageProcessed2.add(input.sessionID);
|
|
8186
|
+
if (isFirstMessage) {
|
|
8187
|
+
log("Skipping keyword detection on first message for title generation", { sessionID: input.sessionID });
|
|
8188
|
+
return;
|
|
8189
|
+
}
|
|
8032
8190
|
const promptText = extractPromptText2(output.parts);
|
|
8033
8191
|
const messages = detectKeywords(promptText);
|
|
8034
8192
|
if (messages.length === 0) {
|
|
@@ -8058,7 +8216,13 @@ var NON_INTERACTIVE_ENV = {
|
|
|
8058
8216
|
DEBIAN_FRONTEND: "noninteractive",
|
|
8059
8217
|
GIT_TERMINAL_PROMPT: "0",
|
|
8060
8218
|
GCM_INTERACTIVE: "never",
|
|
8061
|
-
HOMEBREW_NO_AUTO_UPDATE: "1"
|
|
8219
|
+
HOMEBREW_NO_AUTO_UPDATE: "1",
|
|
8220
|
+
GIT_EDITOR: "true",
|
|
8221
|
+
EDITOR: "true",
|
|
8222
|
+
VISUAL: "true",
|
|
8223
|
+
GIT_SEQUENCE_EDITOR: "true",
|
|
8224
|
+
GIT_PAGER: "cat",
|
|
8225
|
+
PAGER: "cat"
|
|
8062
8226
|
};
|
|
8063
8227
|
|
|
8064
8228
|
// src/hooks/non-interactive-env/index.ts
|
|
@@ -8088,7 +8252,7 @@ import {
|
|
|
8088
8252
|
existsSync as existsSync22,
|
|
8089
8253
|
mkdirSync as mkdirSync9,
|
|
8090
8254
|
readFileSync as readFileSync14,
|
|
8091
|
-
writeFileSync as
|
|
8255
|
+
writeFileSync as writeFileSync11,
|
|
8092
8256
|
unlinkSync as unlinkSync8
|
|
8093
8257
|
} from "fs";
|
|
8094
8258
|
import { join as join32 } from "path";
|
|
@@ -8136,7 +8300,7 @@ function saveInteractiveBashSessionState(state2) {
|
|
|
8136
8300
|
tmuxSessions: Array.from(state2.tmuxSessions),
|
|
8137
8301
|
updatedAt: state2.updatedAt
|
|
8138
8302
|
};
|
|
8139
|
-
|
|
8303
|
+
writeFileSync11(filePath, JSON.stringify(serialized, null, 2));
|
|
8140
8304
|
}
|
|
8141
8305
|
function clearInteractiveBashSessionState(sessionID) {
|
|
8142
8306
|
const filePath = getStoragePath5(sessionID);
|
|
@@ -10525,6 +10689,10 @@ var BUILTIN_SERVERS = {
|
|
|
10525
10689
|
command: ["astro-ls", "--stdio"],
|
|
10526
10690
|
extensions: [".astro"]
|
|
10527
10691
|
},
|
|
10692
|
+
"bash-ls": {
|
|
10693
|
+
command: ["bash-language-server", "start"],
|
|
10694
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"]
|
|
10695
|
+
},
|
|
10528
10696
|
jdtls: {
|
|
10529
10697
|
command: ["jdtls"],
|
|
10530
10698
|
extensions: [".java"]
|
|
@@ -11311,7 +11479,7 @@ ${msg}`);
|
|
|
11311
11479
|
}
|
|
11312
11480
|
// src/tools/lsp/utils.ts
|
|
11313
11481
|
import { extname as extname2, resolve as resolve6 } from "path";
|
|
11314
|
-
import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as
|
|
11482
|
+
import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
|
|
11315
11483
|
function findWorkspaceRoot(filePath) {
|
|
11316
11484
|
let dir = resolve6(filePath);
|
|
11317
11485
|
if (!existsSync28(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
@@ -11503,7 +11671,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
11503
11671
|
`));
|
|
11504
11672
|
}
|
|
11505
11673
|
}
|
|
11506
|
-
|
|
11674
|
+
writeFileSync12(filePath, lines.join(`
|
|
11507
11675
|
`), "utf-8");
|
|
11508
11676
|
return { success: true, editCount: edits.length };
|
|
11509
11677
|
} catch (err) {
|
|
@@ -11534,7 +11702,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
11534
11702
|
if (change.kind === "create") {
|
|
11535
11703
|
try {
|
|
11536
11704
|
const filePath = change.uri.replace("file://", "");
|
|
11537
|
-
|
|
11705
|
+
writeFileSync12(filePath, "", "utf-8");
|
|
11538
11706
|
result.filesModified.push(filePath);
|
|
11539
11707
|
} catch (err) {
|
|
11540
11708
|
result.success = false;
|
|
@@ -11545,7 +11713,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
11545
11713
|
const oldPath = change.oldUri.replace("file://", "");
|
|
11546
11714
|
const newPath = change.newUri.replace("file://", "");
|
|
11547
11715
|
const content = readFileSync20(oldPath, "utf-8");
|
|
11548
|
-
|
|
11716
|
+
writeFileSync12(newPath, content, "utf-8");
|
|
11549
11717
|
__require("fs").unlinkSync(oldPath);
|
|
11550
11718
|
result.filesModified.push(newPath);
|
|
11551
11719
|
} catch (err) {
|
|
@@ -26799,7 +26967,8 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
26799
26967
|
claude_code: ClaudeCodeConfigSchema.optional(),
|
|
26800
26968
|
google_auth: exports_external.boolean().optional(),
|
|
26801
26969
|
sisyphus_agent: SisyphusAgentConfigSchema.optional(),
|
|
26802
|
-
experimental: ExperimentalConfigSchema.optional()
|
|
26970
|
+
experimental: ExperimentalConfigSchema.optional(),
|
|
26971
|
+
auto_update: exports_external.boolean().optional()
|
|
26803
26972
|
});
|
|
26804
26973
|
// src/agents/plan-prompt.ts
|
|
26805
26974
|
var PLAN_SYSTEM_PROMPT = `<system-reminder>
|
|
@@ -27018,7 +27187,8 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27018
27187
|
const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
|
|
27019
27188
|
const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx, {
|
|
27020
27189
|
showStartupToast: isHookEnabled("startup-toast"),
|
|
27021
|
-
isSisyphusEnabled: pluginConfig.sisyphus_agent?.disabled !== true
|
|
27190
|
+
isSisyphusEnabled: pluginConfig.sisyphus_agent?.disabled !== true,
|
|
27191
|
+
autoUpdate: pluginConfig.auto_update ?? true
|
|
27022
27192
|
}) : null;
|
|
27023
27193
|
const keywordDetector = isHookEnabled("keyword-detector") ? createKeywordDetectorHook() : null;
|
|
27024
27194
|
const agentUsageReminder = isHookEnabled("agent-usage-reminder") ? createAgentUsageReminderHook(ctx) : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
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",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"build:schema": "bun run script/build-schema.ts",
|
|
25
25
|
"clean": "rm -rf dist",
|
|
26
26
|
"prepublishOnly": "bun run clean && bun run build",
|
|
27
|
-
"typecheck": "tsc --noEmit"
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "bun test"
|
|
28
29
|
},
|
|
29
30
|
"keywords": [
|
|
30
31
|
"opencode",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
-
export declare function createGrepOutputTruncatorHook(ctx: PluginInput): {
|
|
3
|
-
"tool.execute.after": (input: {
|
|
4
|
-
tool: string;
|
|
5
|
-
sessionID: string;
|
|
6
|
-
callID: string;
|
|
7
|
-
}, output: {
|
|
8
|
-
title: string;
|
|
9
|
-
output: string;
|
|
10
|
-
metadata: unknown;
|
|
11
|
-
}) => Promise<void>;
|
|
12
|
-
};
|