oh-my-opencode 2.3.1 → 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/index.js +311 -177
- package/package.json +3 -2
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/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")
|
|
@@ -5710,12 +5749,16 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
|
|
|
5710
5749
|
});
|
|
5711
5750
|
fallbackState.revertAttempt++;
|
|
5712
5751
|
fallbackState.lastRevertedMessageID = pair.userMessageID;
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
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);
|
|
5719
5762
|
return;
|
|
5720
5763
|
} catch {}
|
|
5721
5764
|
} else {
|
|
@@ -7504,9 +7547,6 @@ var USER_OPENCODE_CONFIG = path4.join(USER_CONFIG_DIR, "opencode", "opencode.jso
|
|
|
7504
7547
|
var USER_OPENCODE_CONFIG_JSONC = path4.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
|
|
7505
7548
|
|
|
7506
7549
|
// src/hooks/auto-update-checker/checker.ts
|
|
7507
|
-
function isLocalDevMode(directory) {
|
|
7508
|
-
return getLocalDevPath(directory) !== null;
|
|
7509
|
-
}
|
|
7510
7550
|
function stripJsonComments(json) {
|
|
7511
7551
|
return json.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m).replace(/,(\s*[}\]])/g, "$1");
|
|
7512
7552
|
}
|
|
@@ -7588,12 +7628,12 @@ function findPluginEntry(directory) {
|
|
|
7588
7628
|
const plugins = config.plugin ?? [];
|
|
7589
7629
|
for (const entry of plugins) {
|
|
7590
7630
|
if (entry === PACKAGE_NAME) {
|
|
7591
|
-
return { entry, isPinned: false, pinnedVersion: null };
|
|
7631
|
+
return { entry, isPinned: false, pinnedVersion: null, configPath };
|
|
7592
7632
|
}
|
|
7593
7633
|
if (entry.startsWith(`${PACKAGE_NAME}@`)) {
|
|
7594
7634
|
const pinnedVersion = entry.slice(PACKAGE_NAME.length + 1);
|
|
7595
7635
|
const isPinned = pinnedVersion !== "latest";
|
|
7596
|
-
return { entry, isPinned, pinnedVersion: isPinned ? pinnedVersion : null };
|
|
7636
|
+
return { entry, isPinned, pinnedVersion: isPinned ? pinnedVersion : null, configPath };
|
|
7597
7637
|
}
|
|
7598
7638
|
}
|
|
7599
7639
|
} catch {
|
|
@@ -7625,6 +7665,48 @@ function getCachedVersion() {
|
|
|
7625
7665
|
}
|
|
7626
7666
|
return null;
|
|
7627
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
|
+
}
|
|
7628
7710
|
async function getLatestVersion() {
|
|
7629
7711
|
const controller = new AbortController;
|
|
7630
7712
|
const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
|
|
@@ -7643,34 +7725,6 @@ async function getLatestVersion() {
|
|
|
7643
7725
|
clearTimeout(timeoutId);
|
|
7644
7726
|
}
|
|
7645
7727
|
}
|
|
7646
|
-
async function checkForUpdate(directory) {
|
|
7647
|
-
if (isLocalDevMode(directory)) {
|
|
7648
|
-
log("[auto-update-checker] Local dev mode detected, skipping update check");
|
|
7649
|
-
return { needsUpdate: false, currentVersion: null, latestVersion: null, isLocalDev: true, isPinned: false };
|
|
7650
|
-
}
|
|
7651
|
-
const pluginInfo = findPluginEntry(directory);
|
|
7652
|
-
if (!pluginInfo) {
|
|
7653
|
-
log("[auto-update-checker] Plugin not found in config");
|
|
7654
|
-
return { needsUpdate: false, currentVersion: null, latestVersion: null, isLocalDev: false, isPinned: false };
|
|
7655
|
-
}
|
|
7656
|
-
if (pluginInfo.isPinned) {
|
|
7657
|
-
log(`[auto-update-checker] Version pinned to ${pluginInfo.pinnedVersion}, skipping update check`);
|
|
7658
|
-
return { needsUpdate: false, currentVersion: pluginInfo.pinnedVersion, latestVersion: null, isLocalDev: false, isPinned: true };
|
|
7659
|
-
}
|
|
7660
|
-
const currentVersion = getCachedVersion();
|
|
7661
|
-
if (!currentVersion) {
|
|
7662
|
-
log("[auto-update-checker] No cached version found");
|
|
7663
|
-
return { needsUpdate: false, currentVersion: null, latestVersion: null, isLocalDev: false, isPinned: false };
|
|
7664
|
-
}
|
|
7665
|
-
const latestVersion = await getLatestVersion();
|
|
7666
|
-
if (!latestVersion) {
|
|
7667
|
-
log("[auto-update-checker] Failed to fetch latest version");
|
|
7668
|
-
return { needsUpdate: false, currentVersion, latestVersion: null, isLocalDev: false, isPinned: false };
|
|
7669
|
-
}
|
|
7670
|
-
const needsUpdate = currentVersion !== latestVersion;
|
|
7671
|
-
log(`[auto-update-checker] Current: ${currentVersion}, Latest: ${latestVersion}, NeedsUpdate: ${needsUpdate}`);
|
|
7672
|
-
return { needsUpdate, currentVersion, latestVersion, isLocalDev: false, isPinned: false };
|
|
7673
|
-
}
|
|
7674
7728
|
|
|
7675
7729
|
// src/hooks/auto-update-checker/cache.ts
|
|
7676
7730
|
import * as fs5 from "fs";
|
|
@@ -7739,7 +7793,7 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
|
7739
7793
|
|
|
7740
7794
|
// src/hooks/auto-update-checker/index.ts
|
|
7741
7795
|
function createAutoUpdateCheckerHook(ctx, options = {}) {
|
|
7742
|
-
const { showStartupToast = true, isSisyphusEnabled = false } = options;
|
|
7796
|
+
const { showStartupToast = true, isSisyphusEnabled = false, autoUpdate = true } = options;
|
|
7743
7797
|
const getToastMessage = (isUpdate, latestVersion) => {
|
|
7744
7798
|
if (isSisyphusEnabled) {
|
|
7745
7799
|
return isUpdate ? `Sisyphus on steroids is steering OpenCode.
|
|
@@ -7748,21 +7802,9 @@ v${latestVersion} available. Restart to apply.` : `Sisyphus on steroids is steer
|
|
|
7748
7802
|
return isUpdate ? `OpenCode is now on Steroids. oMoMoMoMo...
|
|
7749
7803
|
v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on Steroids. oMoMoMoMo...`;
|
|
7750
7804
|
};
|
|
7751
|
-
const showVersionToast = async (version) => {
|
|
7752
|
-
const displayVersion = version ?? "unknown";
|
|
7753
|
-
await ctx.client.tui.showToast({
|
|
7754
|
-
body: {
|
|
7755
|
-
title: `OhMyOpenCode ${displayVersion}`,
|
|
7756
|
-
message: getToastMessage(false),
|
|
7757
|
-
variant: "info",
|
|
7758
|
-
duration: 5000
|
|
7759
|
-
}
|
|
7760
|
-
}).catch(() => {});
|
|
7761
|
-
log(`[auto-update-checker] Startup toast shown: v${displayVersion}`);
|
|
7762
|
-
};
|
|
7763
7805
|
let hasChecked = false;
|
|
7764
7806
|
return {
|
|
7765
|
-
event:
|
|
7807
|
+
event: ({ event }) => {
|
|
7766
7808
|
if (event.type !== "session.created")
|
|
7767
7809
|
return;
|
|
7768
7810
|
if (hasChecked)
|
|
@@ -7771,47 +7813,69 @@ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on S
|
|
|
7771
7813
|
if (props?.info?.parentID)
|
|
7772
7814
|
return;
|
|
7773
7815
|
hasChecked = true;
|
|
7774
|
-
|
|
7775
|
-
const
|
|
7776
|
-
|
|
7777
|
-
|
|
7816
|
+
setTimeout(() => {
|
|
7817
|
+
const cachedVersion = getCachedVersion();
|
|
7818
|
+
const localDevVersion = getLocalDevVersion(ctx.directory);
|
|
7819
|
+
const displayVersion = localDevVersion ?? cachedVersion;
|
|
7820
|
+
showConfigErrorsIfAny(ctx).catch(() => {});
|
|
7821
|
+
if (localDevVersion) {
|
|
7778
7822
|
if (showStartupToast) {
|
|
7779
|
-
|
|
7780
|
-
await showVersionToast(version);
|
|
7823
|
+
showLocalDevToast(ctx, displayVersion, isSisyphusEnabled).catch(() => {});
|
|
7781
7824
|
}
|
|
7825
|
+
log("[auto-update-checker] Local development mode");
|
|
7782
7826
|
return;
|
|
7783
7827
|
}
|
|
7784
|
-
if (
|
|
7785
|
-
|
|
7786
|
-
if (showStartupToast) {
|
|
7787
|
-
await showVersionToast(result.currentVersion);
|
|
7788
|
-
}
|
|
7789
|
-
return;
|
|
7828
|
+
if (showStartupToast) {
|
|
7829
|
+
showVersionToast(ctx, displayVersion, getToastMessage(false)).catch(() => {});
|
|
7790
7830
|
}
|
|
7791
|
-
|
|
7792
|
-
log("[auto-update-checker]
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
}
|
|
7796
|
-
return;
|
|
7797
|
-
}
|
|
7798
|
-
invalidatePackage(PACKAGE_NAME);
|
|
7799
|
-
await ctx.client.tui.showToast({
|
|
7800
|
-
body: {
|
|
7801
|
-
title: `OhMyOpenCode ${result.latestVersion}`,
|
|
7802
|
-
message: getToastMessage(true, result.latestVersion ?? undefined),
|
|
7803
|
-
variant: "info",
|
|
7804
|
-
duration: 8000
|
|
7805
|
-
}
|
|
7806
|
-
}).catch(() => {});
|
|
7807
|
-
log(`[auto-update-checker] Update notification sent: v${result.currentVersion} \u2192 v${result.latestVersion}`);
|
|
7808
|
-
} catch (err) {
|
|
7809
|
-
log("[auto-update-checker] Error during update check:", err);
|
|
7810
|
-
}
|
|
7811
|
-
await showConfigErrorsIfAny(ctx);
|
|
7831
|
+
runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage).catch((err) => {
|
|
7832
|
+
log("[auto-update-checker] Background update check failed:", err);
|
|
7833
|
+
});
|
|
7834
|
+
}, 0);
|
|
7812
7835
|
}
|
|
7813
7836
|
};
|
|
7814
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
|
+
}
|
|
7815
7879
|
async function showConfigErrorsIfAny(ctx) {
|
|
7816
7880
|
const errors = getConfigLoadErrors();
|
|
7817
7881
|
if (errors.length === 0)
|
|
@@ -7830,12 +7894,60 @@ ${errorMessages}`,
|
|
|
7830
7894
|
log(`[auto-update-checker] Config load errors shown: ${errors.length} error(s)`);
|
|
7831
7895
|
clearConfigLoadErrors();
|
|
7832
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
|
+
}
|
|
7833
7945
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
7834
7946
|
import {
|
|
7835
7947
|
existsSync as existsSync21,
|
|
7836
7948
|
mkdirSync as mkdirSync8,
|
|
7837
7949
|
readFileSync as readFileSync13,
|
|
7838
|
-
writeFileSync as
|
|
7950
|
+
writeFileSync as writeFileSync10,
|
|
7839
7951
|
unlinkSync as unlinkSync7
|
|
7840
7952
|
} from "fs";
|
|
7841
7953
|
import { join as join30 } from "path";
|
|
@@ -7906,7 +8018,7 @@ function saveAgentUsageState(state2) {
|
|
|
7906
8018
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
7907
8019
|
}
|
|
7908
8020
|
const filePath = getStoragePath4(state2.sessionID);
|
|
7909
|
-
|
|
8021
|
+
writeFileSync10(filePath, JSON.stringify(state2, null, 2));
|
|
7910
8022
|
}
|
|
7911
8023
|
function clearAgentUsageState(sessionID) {
|
|
7912
8024
|
const filePath = getStoragePath4(sessionID);
|
|
@@ -8012,6 +8124,14 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
|
8012
8124
|
3. Always Use Plan agent with gathered context to create detailed work breakdown
|
|
8013
8125
|
4. Execute with continuous verification against original requirements
|
|
8014
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
|
+
|
|
8015
8135
|
</ultrawork-mode>
|
|
8016
8136
|
|
|
8017
8137
|
---
|
|
@@ -8030,18 +8150,17 @@ NEVER stop at first result - be exhaustive.`
|
|
|
8030
8150
|
{
|
|
8031
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,
|
|
8032
8152
|
message: `[analyze-mode]
|
|
8033
|
-
|
|
8153
|
+
ANALYSIS MODE. Gather context before diving deep:
|
|
8034
8154
|
|
|
8035
|
-
|
|
8036
|
-
-
|
|
8037
|
-
-
|
|
8038
|
-
-
|
|
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
|
|
8039
8159
|
|
|
8040
|
-
|
|
8041
|
-
-
|
|
8042
|
-
- Each oracle: different angle (architecture, performance, edge cases)
|
|
8160
|
+
IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
|
|
8161
|
+
- Consult oracle for strategic guidance
|
|
8043
8162
|
|
|
8044
|
-
SYNTHESIZE
|
|
8163
|
+
SYNTHESIZE findings before proceeding.`
|
|
8045
8164
|
}
|
|
8046
8165
|
];
|
|
8047
8166
|
|
|
@@ -8058,9 +8177,16 @@ function extractPromptText2(parts) {
|
|
|
8058
8177
|
}
|
|
8059
8178
|
|
|
8060
8179
|
// src/hooks/keyword-detector/index.ts
|
|
8180
|
+
var sessionFirstMessageProcessed2 = new Set;
|
|
8061
8181
|
function createKeywordDetectorHook() {
|
|
8062
8182
|
return {
|
|
8063
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
|
+
}
|
|
8064
8190
|
const promptText = extractPromptText2(output.parts);
|
|
8065
8191
|
const messages = detectKeywords(promptText);
|
|
8066
8192
|
if (messages.length === 0) {
|
|
@@ -8090,7 +8216,13 @@ var NON_INTERACTIVE_ENV = {
|
|
|
8090
8216
|
DEBIAN_FRONTEND: "noninteractive",
|
|
8091
8217
|
GIT_TERMINAL_PROMPT: "0",
|
|
8092
8218
|
GCM_INTERACTIVE: "never",
|
|
8093
|
-
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"
|
|
8094
8226
|
};
|
|
8095
8227
|
|
|
8096
8228
|
// src/hooks/non-interactive-env/index.ts
|
|
@@ -8120,7 +8252,7 @@ import {
|
|
|
8120
8252
|
existsSync as existsSync22,
|
|
8121
8253
|
mkdirSync as mkdirSync9,
|
|
8122
8254
|
readFileSync as readFileSync14,
|
|
8123
|
-
writeFileSync as
|
|
8255
|
+
writeFileSync as writeFileSync11,
|
|
8124
8256
|
unlinkSync as unlinkSync8
|
|
8125
8257
|
} from "fs";
|
|
8126
8258
|
import { join as join32 } from "path";
|
|
@@ -8168,7 +8300,7 @@ function saveInteractiveBashSessionState(state2) {
|
|
|
8168
8300
|
tmuxSessions: Array.from(state2.tmuxSessions),
|
|
8169
8301
|
updatedAt: state2.updatedAt
|
|
8170
8302
|
};
|
|
8171
|
-
|
|
8303
|
+
writeFileSync11(filePath, JSON.stringify(serialized, null, 2));
|
|
8172
8304
|
}
|
|
8173
8305
|
function clearInteractiveBashSessionState(sessionID) {
|
|
8174
8306
|
const filePath = getStoragePath5(sessionID);
|
|
@@ -11347,7 +11479,7 @@ ${msg}`);
|
|
|
11347
11479
|
}
|
|
11348
11480
|
// src/tools/lsp/utils.ts
|
|
11349
11481
|
import { extname as extname2, resolve as resolve6 } from "path";
|
|
11350
|
-
import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as
|
|
11482
|
+
import { existsSync as existsSync28, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
|
|
11351
11483
|
function findWorkspaceRoot(filePath) {
|
|
11352
11484
|
let dir = resolve6(filePath);
|
|
11353
11485
|
if (!existsSync28(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
@@ -11539,7 +11671,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
11539
11671
|
`));
|
|
11540
11672
|
}
|
|
11541
11673
|
}
|
|
11542
|
-
|
|
11674
|
+
writeFileSync12(filePath, lines.join(`
|
|
11543
11675
|
`), "utf-8");
|
|
11544
11676
|
return { success: true, editCount: edits.length };
|
|
11545
11677
|
} catch (err) {
|
|
@@ -11570,7 +11702,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
11570
11702
|
if (change.kind === "create") {
|
|
11571
11703
|
try {
|
|
11572
11704
|
const filePath = change.uri.replace("file://", "");
|
|
11573
|
-
|
|
11705
|
+
writeFileSync12(filePath, "", "utf-8");
|
|
11574
11706
|
result.filesModified.push(filePath);
|
|
11575
11707
|
} catch (err) {
|
|
11576
11708
|
result.success = false;
|
|
@@ -11581,7 +11713,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
11581
11713
|
const oldPath = change.oldUri.replace("file://", "");
|
|
11582
11714
|
const newPath = change.newUri.replace("file://", "");
|
|
11583
11715
|
const content = readFileSync20(oldPath, "utf-8");
|
|
11584
|
-
|
|
11716
|
+
writeFileSync12(newPath, content, "utf-8");
|
|
11585
11717
|
__require("fs").unlinkSync(oldPath);
|
|
11586
11718
|
result.filesModified.push(newPath);
|
|
11587
11719
|
} catch (err) {
|
|
@@ -26835,7 +26967,8 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
26835
26967
|
claude_code: ClaudeCodeConfigSchema.optional(),
|
|
26836
26968
|
google_auth: exports_external.boolean().optional(),
|
|
26837
26969
|
sisyphus_agent: SisyphusAgentConfigSchema.optional(),
|
|
26838
|
-
experimental: ExperimentalConfigSchema.optional()
|
|
26970
|
+
experimental: ExperimentalConfigSchema.optional(),
|
|
26971
|
+
auto_update: exports_external.boolean().optional()
|
|
26839
26972
|
});
|
|
26840
26973
|
// src/agents/plan-prompt.ts
|
|
26841
26974
|
var PLAN_SYSTEM_PROMPT = `<system-reminder>
|
|
@@ -27054,7 +27187,8 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27054
27187
|
const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
|
|
27055
27188
|
const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx, {
|
|
27056
27189
|
showStartupToast: isHookEnabled("startup-toast"),
|
|
27057
|
-
isSisyphusEnabled: pluginConfig.sisyphus_agent?.disabled !== true
|
|
27190
|
+
isSisyphusEnabled: pluginConfig.sisyphus_agent?.disabled !== true,
|
|
27191
|
+
autoUpdate: pluginConfig.auto_update ?? true
|
|
27058
27192
|
}) : null;
|
|
27059
27193
|
const keywordDetector = isHookEnabled("keyword-detector") ? createKeywordDetectorHook() : null;
|
|
27060
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",
|