opencode-ultra 0.4.0 → 0.5.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.md +152 -264
- package/dist/hooks/keyword-detector.d.ts +1 -1
- package/dist/hooks/prompt-renderer.d.ts +3 -2
- package/dist/hooks/session-compaction.d.ts +1 -1
- package/dist/index.js +131 -8
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/ttl-map.d.ts +19 -0
- package/dist/shared/types.d.ts +93 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,30 +1,52 @@
|
|
|
1
1
|
# opencode-ultra
|
|
2
2
|
|
|
3
|
-
[oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
##
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
|
11
|
-
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
[oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) をベースに、micode / opencode-skillful の良い部分を取り込んだ OpenCode 1.2.x プラグイン。
|
|
4
|
+
マルチエージェントオーケストレーション・キーワード駆動モード切替・ルール注入・セッション継続・AST検索を軽量な単一プラグインで実現する。
|
|
5
|
+
|
|
6
|
+
## 機能一覧
|
|
7
|
+
|
|
8
|
+
### ツール (7)
|
|
9
|
+
|
|
10
|
+
| ツール | 説明 |
|
|
11
|
+
|--------|------|
|
|
12
|
+
| `spawn_agent` | 並列エージェント実行 (ConcurrencyPool 制御付き) |
|
|
13
|
+
| `ralph_loop` | 自律ループ実行 (`<promise>DONE</promise>` で完了検知) |
|
|
14
|
+
| `cancel_ralph` | 実行中の Ralph Loop をキャンセル |
|
|
15
|
+
| `batch_read` | 複数ファイル並列読み込み (最大20) |
|
|
16
|
+
| `ledger_save` | Continuity Ledger の保存 (.opencode/ledgers/) |
|
|
17
|
+
| `ledger_load` | Continuity Ledger の読み込み (名前指定 or 最新) |
|
|
18
|
+
| `ast_search` | AST-aware コード検索 (ast-grep/sg バイナリ使用、未インストール時は自動スキップ) |
|
|
19
|
+
|
|
20
|
+
### フック (9)
|
|
21
|
+
|
|
22
|
+
| フック | hook ポイント | 説明 |
|
|
23
|
+
|--------|-------------|------|
|
|
24
|
+
| `keyword-detector` | chat.message | ultrawork/search/analyze/think キーワード検知 |
|
|
25
|
+
| `rules-injector` | system.transform | .opencode/rules.md を system prompt に注入 |
|
|
26
|
+
| `context-injector` | system.transform | ARCHITECTURE.md / CODE_STYLE.md を自動注入 |
|
|
27
|
+
| `fragment-injector` | system.transform | エージェント毎のカスタムプロンプト断片注入 |
|
|
28
|
+
| `prompt-renderer` | system.transform | モデル別 system prompt フォーマット最適化 (markdown/xml/json) |
|
|
29
|
+
| `comment-checker` | tool.execute.after | Write/Edit 後の AI スロップコメント検知 |
|
|
30
|
+
| `token-truncation` | tool.execute.after | 30000文字超のツール出力を先頭40%+末尾40%に圧縮 |
|
|
31
|
+
| `todo-enforcer` | event (session.idle) | 未完了 TODO を検知して強制継続 |
|
|
32
|
+
| `session-compaction` | experimental.session.compacting | セッション圧縮時に structured summary 生成 |
|
|
33
|
+
|
|
34
|
+
### その他
|
|
35
|
+
|
|
36
|
+
| 機能 | 説明 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| **Built-in Agent Demotion** | OpenCode 標準エージェント (build/plan/triage/docs) を subagent に降格 |
|
|
39
|
+
| **MCP 自動登録** | Context7 を自動登録 (API キー任意) |
|
|
40
|
+
| **Plan-First Protocol** | spawn_agent 前に Phase 形式のプランを提示、ユーザー承認を待つ |
|
|
41
|
+
| **Categories** | spawn_agent の category パラメータでモデル/バリアントを一括切替 |
|
|
42
|
+
| **Concurrency Pool** | Semaphore ベースの global/provider/model 三層並列制御 |
|
|
21
43
|
|
|
22
44
|
## アーキテクチャ
|
|
23
45
|
|
|
24
46
|
```
|
|
25
47
|
User
|
|
26
48
|
│
|
|
27
|
-
│ "ultrawork" / "ulw" キーワード
|
|
49
|
+
│ "ultrawork" / "ulw" / "think hard" キーワード
|
|
28
50
|
│
|
|
29
51
|
▼
|
|
30
52
|
┌──────────────────────────────────────────────┐
|
|
@@ -32,27 +54,37 @@ User
|
|
|
32
54
|
│ │
|
|
33
55
|
│ chat.message hook │
|
|
34
56
|
│ ├─ keyword-detector │
|
|
35
|
-
│ │ └─ ultrawork / search / analyze
|
|
57
|
+
│ │ └─ ultrawork / search / analyze / think │
|
|
36
58
|
│ └─ ultrawork → variant: "max" │
|
|
37
59
|
│ │
|
|
38
60
|
│ system.transform hook │
|
|
39
61
|
│ ├─ keyword message injection │
|
|
40
|
-
│
|
|
62
|
+
│ ├─ rules-injector (.opencode/rules.md) │
|
|
63
|
+
│ ├─ context-injector (ARCHITECTURE/CODE_STYLE)│
|
|
64
|
+
│ ├─ fragment-injector (per-agent fragments) │
|
|
65
|
+
│ └─ prompt-renderer (markdown/xml/json) │
|
|
41
66
|
│ │
|
|
42
67
|
│ tool.execute.after hook │
|
|
43
|
-
│
|
|
68
|
+
│ ├─ comment-checker (Write/Edit 後に検査) │
|
|
69
|
+
│ └─ token-truncation (30000文字超を圧縮) │
|
|
70
|
+
│ │
|
|
71
|
+
│ session.compacting hook │
|
|
72
|
+
│ └─ session-compaction (structured summary) │
|
|
44
73
|
│ │
|
|
45
74
|
│ event hook │
|
|
46
75
|
│ ├─ session cleanup │
|
|
47
76
|
│ └─ todo-enforcer (session.idle 時に検査) │
|
|
48
77
|
│ │
|
|
49
78
|
│ config hook │
|
|
50
|
-
│
|
|
79
|
+
│ ├─ register agents to OpenCode │
|
|
80
|
+
│ └─ demote built-in agents to subagent │
|
|
51
81
|
│ │
|
|
52
82
|
│ tools │
|
|
53
83
|
│ ├─ spawn_agent (並列実行 + 並列制御) │
|
|
54
|
-
│ ├─ ralph_loop
|
|
55
|
-
│
|
|
84
|
+
│ ├─ ralph_loop / cancel_ralph │
|
|
85
|
+
│ ├─ batch_read (複数ファイル並列読み込み) │
|
|
86
|
+
│ ├─ ledger_save / ledger_load (文脈継続) │
|
|
87
|
+
│ └─ ast_search (構文木検索, optional) │
|
|
56
88
|
│ │
|
|
57
89
|
│ MCP registration │
|
|
58
90
|
│ └─ context7 (自動登録) │
|
|
@@ -72,13 +104,13 @@ User
|
|
|
72
104
|
|
|
73
105
|
## エージェント構成
|
|
74
106
|
|
|
75
|
-
Sisyphus (オーケストレーター)
|
|
107
|
+
Sisyphus (オーケストレーター) が直接コードを読み、実装はサブエージェントに委任する。
|
|
76
108
|
|
|
77
109
|
### ビルトインデフォルト
|
|
78
110
|
|
|
79
111
|
| Agent | 役割 | デフォルトモデル | モード |
|
|
80
112
|
|-------|------|-----------------|--------|
|
|
81
|
-
| **sisyphus** | オーケストレーター —
|
|
113
|
+
| **sisyphus** | オーケストレーター — 読み込み+分析+計画+委任 | openai-codex/gpt-5.3-codex | primary |
|
|
82
114
|
| **oracle** | 設計・デバッグ・アーキテクチャ判断 | openai/gpt-5.2 | subagent |
|
|
83
115
|
| **explore** | 高速コードベース偵察 | anthropic/claude-haiku-4-5 | subagent |
|
|
84
116
|
| **librarian** | ドキュメント・ベストプラクティス調査 | anthropic/claude-sonnet-4-5 | subagent |
|
|
@@ -88,314 +120,175 @@ Sisyphus (オーケストレーター) が直接ツールを使わず、全て
|
|
|
88
120
|
| **atlas** | タスク管理・進捗追跡 | anthropic/claude-sonnet-4-5 | subagent |
|
|
89
121
|
| **multimodal-looker** | 画像・スクリーンショット解析 | anthropic/claude-sonnet-4-5 | subagent |
|
|
90
122
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
### Sisyphus の制約
|
|
123
|
+
全てのモデルは `oh-my-opencode.json` でオーバーライド可能。
|
|
94
124
|
|
|
95
|
-
Sisyphus
|
|
125
|
+
### Sisyphus のツール権限
|
|
96
126
|
|
|
97
127
|
```
|
|
98
|
-
Grep:
|
|
99
|
-
Write: false, Edit: false, Bash: false
|
|
128
|
+
Grep: true, Glob: true, Read: true ← 直接読み込み可能
|
|
129
|
+
Write: false, Edit: false, Bash: false ← サブエージェントに委任
|
|
100
130
|
```
|
|
101
131
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
## Plan-First Protocol
|
|
132
|
+
## Continuity Ledger
|
|
105
133
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
## Execution Plan
|
|
110
|
-
|
|
111
|
-
### Phase 1: 偵察 (parallel)
|
|
112
|
-
- [ ] API ルート探索 → explore (gpt-5.3-codex-spark)
|
|
113
|
-
- [ ] フレームワーク仕様調査 → librarian (glm-4.7)
|
|
114
|
-
|
|
115
|
-
### Phase 2: 実装 (after Phase 1)
|
|
116
|
-
- [ ] エンドポイント実装 → hephaestus (gpt-5.2)
|
|
117
|
-
|
|
118
|
-
### Phase 3: Review
|
|
119
|
-
- [ ] コードレビュー → momus (gpt-5.3-codex)
|
|
120
|
-
|
|
121
|
-
Estimated agents: 4 | Phases: 3
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
- 独立タスクは同一 Phase にグループ化 (並列実行)
|
|
125
|
-
- 依存関係があるタスクは別 Phase
|
|
126
|
-
- エージェント名 + 実際のモデル名を表示
|
|
127
|
-
- **「このプランで進めますか?」で承認待ち — OK するまで実行しない**
|
|
128
|
-
|
|
129
|
-
## spawn_agent ツール
|
|
130
|
-
|
|
131
|
-
Sisyphus が使う並列エージェント実行ツール。
|
|
134
|
+
セッション間の文脈を引き継ぐマークダウンドキュメント。`.opencode/ledgers/` に保存される。
|
|
132
135
|
|
|
133
136
|
```typescript
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{ agent: "librarian", prompt: "Research Express middleware best practices", description: "Middleware docs" },
|
|
138
|
-
{ agent: "hephaestus", prompt: "Implement the feature", description: "Implementation", category: "deep" }
|
|
139
|
-
]
|
|
140
|
-
})
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
- 複数エージェントは **Promise.all で並列実行**(ConcurrencyPool による同時実行制御付き)
|
|
144
|
-
- 各エージェントは**エフェメラルセッション**で起動 — 完了後に自動削除
|
|
145
|
-
- 内部セッションには `internalSessions` フラグを付与し、**フックの再帰を防止**
|
|
146
|
-
- 進捗は `toolCtx.metadata({ title })` でステータスバーに表示
|
|
147
|
-
- Toast 通知で個別エージェントの完了を通知
|
|
148
|
-
- **`category` パラメータ**でモデル/バリアントをカテゴリから自動解決
|
|
137
|
+
// 保存
|
|
138
|
+
ledger_save({ name: "auth-refactor", content: "## Context\n..." })
|
|
139
|
+
// → Saved to .opencode/ledgers/auth-refactor.md
|
|
149
140
|
|
|
150
|
-
|
|
141
|
+
// 読み込み (名前指定)
|
|
142
|
+
ledger_load({ name: "auth-refactor" })
|
|
151
143
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
spawn_agent に 5 エージェント渡す
|
|
156
|
-
→ defaultConcurrency: 3 なら同時 3 個まで
|
|
157
|
-
→ 残りは空きを待って順次実行
|
|
144
|
+
// 読み込み (最新)
|
|
145
|
+
ledger_load({})
|
|
158
146
|
```
|
|
159
147
|
|
|
160
|
-
|
|
161
|
-
|---------------|------|------|
|
|
162
|
-
| `defaultConcurrency` | 全体の同時実行上限 | 全エージェント合計で N 個まで同時実行 |
|
|
163
|
-
| `providerConcurrency` | プロバイダー別上限 (例: openai: 8) | 同一プロバイダーのエージェントを N 個に制限 |
|
|
164
|
-
| `modelConcurrency` | モデル別上限 (例: gpt-5.2: 2) | 同一モデルのエージェントを N 個に制限 |
|
|
148
|
+
## Fragment Injection
|
|
165
149
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
`background_task` 未設定の場合は制限なし(従来通り全エージェント同時発火)。
|
|
169
|
-
|
|
170
|
-
## Categories
|
|
171
|
-
|
|
172
|
-
spawn_agent の `category` パラメータでモデル/バリアントを一括切替できる。
|
|
173
|
-
|
|
174
|
-
### ビルトインカテゴリ
|
|
175
|
-
|
|
176
|
-
| カテゴリ | 用途 | デフォルトモデル | バリアント |
|
|
177
|
-
|---------|------|-----------------|-----------|
|
|
178
|
-
| `visual-engineering` | フロントエンド/UI実装 | google/gemini-3-pro | — |
|
|
179
|
-
| `ultrabrain` | 最高精度タスク | openai/gpt-5.3-codex | xhigh |
|
|
180
|
-
| `deep` | 深い分析・複雑ロジック | openai/gpt-5.3-codex | medium |
|
|
181
|
-
| `quick` | 高速・低コストタスク | anthropic/claude-haiku-4-5 | — |
|
|
182
|
-
| `writing` | ドキュメント・文章 | zai-coding-plan/glm-4.7 | — |
|
|
183
|
-
|
|
184
|
-
設定ファイルでカスタムカテゴリの追加やビルトインの上書きが可能:
|
|
150
|
+
エージェント毎にカスタムプロンプト断片を注入する。
|
|
185
151
|
|
|
186
152
|
```jsonc
|
|
187
153
|
{
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
"
|
|
154
|
+
"fragments": {
|
|
155
|
+
"hephaestus": [".opencode/fragments/strict-coding.md"],
|
|
156
|
+
"sisyphus": [".opencode/fragments/planning-rules.md"]
|
|
191
157
|
}
|
|
192
158
|
}
|
|
193
159
|
```
|
|
194
160
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
自律ループ実行ツール。エージェントが `<promise>DONE</promise>` を出力するか、最大イテレーション数に達するまで繰り返し実行する。
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
ralph_loop({
|
|
201
|
-
prompt: "Implement all unit tests for the auth module",
|
|
202
|
-
agent: "hephaestus", // default
|
|
203
|
-
maxIterations: 10 // default
|
|
204
|
-
})
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
- 各イテレーションで前回の進捗をレビューし、続きから再開するよう指示
|
|
208
|
-
- ステータスバーに `Ralph Loop [3/10] — hephaestus` のように進捗表示
|
|
209
|
-
- `cancel_ralph` ツールで実行中のループを全てキャンセル可能
|
|
210
|
-
|
|
211
|
-
## Todo Enforcer
|
|
212
|
-
|
|
213
|
-
セッションが idle になったとき、最後の assistant メッセージに未完了の TODO(`- [ ]`)がある場合、強制継続プロンプトを送信する。
|
|
214
|
-
|
|
215
|
-
- **最大 5 回**(デフォルト)の強制で無限ループを防止
|
|
216
|
-
- spawn_agent のサブセッションには適用されない
|
|
217
|
-
- `disabled_hooks: ["todo-enforcer"]` で無効化可能
|
|
218
|
-
- 設定で最大回数を変更可能: `"todo_enforcer": { "maxEnforcements": 3 }`
|
|
219
|
-
|
|
220
|
-
## Comment Checker
|
|
221
|
-
|
|
222
|
-
Write / Edit ツール実行後に、書き込まれたファイルのコメント品質を検査する。
|
|
161
|
+
指定ファイルの内容が該当エージェントの system prompt に追加される。
|
|
223
162
|
|
|
224
|
-
|
|
163
|
+
## Per-model Prompt Renderer
|
|
225
164
|
|
|
226
|
-
|
|
227
|
-
- 80 文字超の行コメント (`// This function does...`)
|
|
228
|
-
- 説明的すぎる冒頭 (`// This`, `// The`, `// We`, `// Here`, `// Note:`)
|
|
229
|
-
- AI プレースホルダー (`// TODO: implement`)
|
|
230
|
-
- lint 回避コメント (`// eslint-disable`)
|
|
231
|
-
- 300 文字超の巨大 JSDoc
|
|
232
|
-
|
|
233
|
-
閾値超えの場合、ツール出力に `[Comment Checker]` 警告を追加する。
|
|
165
|
+
モデルに応じて system prompt のフォーマットを最適化する。
|
|
234
166
|
|
|
235
167
|
```jsonc
|
|
236
168
|
{
|
|
237
|
-
"
|
|
238
|
-
"
|
|
239
|
-
"
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
## MCP 自動登録
|
|
245
|
-
|
|
246
|
-
プラグイン初期化時に以下の MCP サーバーを自動登録する:
|
|
247
|
-
|
|
248
|
-
| MCP | パッケージ | API キー |
|
|
249
|
-
|-----|-----------|----------|
|
|
250
|
-
| **Context7** | `@upstash/context7-mcp` | オプション(あるとレートリミット緩和) |
|
|
251
|
-
|
|
252
|
-
API キーの渡し方:
|
|
253
|
-
|
|
254
|
-
```jsonc
|
|
255
|
-
{
|
|
256
|
-
"mcp_api_keys": {
|
|
257
|
-
"context7": "ctx7sk-..."
|
|
169
|
+
"prompt_renderer": {
|
|
170
|
+
"default": "markdown",
|
|
171
|
+
"model_overrides": {
|
|
172
|
+
"anthropic/claude-sonnet-4-5": "xml",
|
|
173
|
+
"openai/gpt-5.2": "json"
|
|
174
|
+
}
|
|
258
175
|
}
|
|
259
176
|
}
|
|
260
177
|
```
|
|
261
178
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
179
|
+
| フォーマット | 出力例 |
|
|
180
|
+
|-------------|--------|
|
|
181
|
+
| `markdown` | そのまま (デフォルト) |
|
|
182
|
+
| `xml` | `<section name="Rules">...</section>` |
|
|
183
|
+
| `json` | `{"section":"Rules","content":"..."}` |
|
|
265
184
|
|
|
266
185
|
## キーワード検知
|
|
267
186
|
|
|
268
|
-
ユーザーのプロンプトからキーワードを検知し、system prompt に動作モード指示を注入する。
|
|
269
|
-
コードブロック (`` ``` `` / `` ` ``) 内のキーワードは無視する。
|
|
270
|
-
|
|
271
187
|
| キーワード | モード | 効果 |
|
|
272
188
|
|-----------|--------|------|
|
|
273
|
-
| `ultrawork` / `ulw` | ultrawork | variant
|
|
274
|
-
| `search` / `find` / `探して`
|
|
275
|
-
| `analyze` / `調査` /
|
|
189
|
+
| `ultrawork` / `ulw` | ultrawork | variant "max"、全エージェント活用 |
|
|
190
|
+
| `search` / `find` / `探して` 等 | search | 網羅的検索 |
|
|
191
|
+
| `analyze` / `調査` / `debug` 等 | analyze | コンテキスト収集 |
|
|
192
|
+
| `think hard` / `じっくり` / `熟考` 等 | think | Extended thinking 有効化 |
|
|
276
193
|
|
|
277
194
|
日本語・中国語キーワードにも対応。
|
|
278
195
|
|
|
279
|
-
## ルール注入
|
|
280
|
-
|
|
281
|
-
`<project>/.opencode/rules.md` が存在する場合、その内容を system prompt に `## Project Rules` として自動注入する。ファイルの mtime でキャッシュし、変更時のみ再読み込み。
|
|
282
|
-
|
|
283
196
|
## 設定
|
|
284
197
|
|
|
285
|
-
2
|
|
198
|
+
2段階の設定マージ:
|
|
286
199
|
|
|
287
200
|
1. **ユーザー設定**: `~/.config/opencode/oh-my-opencode.json[c]`
|
|
288
201
|
2. **プロジェクト設定**: `<project>/.opencode/oh-my-opencode.json[c]`
|
|
289
202
|
|
|
290
|
-
プロジェクト設定がユーザー設定をオーバーライドする。
|
|
291
|
-
|
|
292
203
|
### 設定スキーマ
|
|
293
204
|
|
|
294
205
|
```jsonc
|
|
295
206
|
{
|
|
296
|
-
//
|
|
207
|
+
// エージェントオーバーライド
|
|
297
208
|
"agents": {
|
|
298
209
|
"sisyphus": { "model": "openai/gpt-5.2" },
|
|
299
|
-
"oracle": { "model": "zai-coding-plan/glm-5" }
|
|
300
|
-
"explore": { "model": "openai/gpt-5.3-codex-spark" }
|
|
210
|
+
"oracle": { "model": "zai-coding-plan/glm-5" }
|
|
301
211
|
},
|
|
302
212
|
|
|
303
|
-
// カテゴリ
|
|
213
|
+
// カテゴリ
|
|
304
214
|
"categories": {
|
|
305
215
|
"quick": { "model": "openai/gpt-4o-mini" }
|
|
306
216
|
},
|
|
307
217
|
|
|
218
|
+
// Fragment Injection
|
|
219
|
+
"fragments": {
|
|
220
|
+
"hephaestus": [".opencode/fragments/strict.md"]
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
// Per-model Renderer
|
|
224
|
+
"prompt_renderer": {
|
|
225
|
+
"default": "markdown",
|
|
226
|
+
"model_overrides": {}
|
|
227
|
+
},
|
|
228
|
+
|
|
308
229
|
// 無効化
|
|
309
230
|
"disabled_agents": [],
|
|
310
|
-
"disabled_hooks": [
|
|
311
|
-
|
|
312
|
-
|
|
231
|
+
"disabled_hooks": [], // keyword-detector, rules-injector, context-injector,
|
|
232
|
+
// fragment-injector, prompt-renderer, comment-checker,
|
|
233
|
+
// token-truncation, todo-enforcer, session-compaction
|
|
234
|
+
"disabled_tools": [], // spawn_agent, ralph_loop, cancel_ralph,
|
|
235
|
+
// batch_read, ledger_save, ledger_load, ast_search
|
|
236
|
+
"disabled_mcps": [], // context7
|
|
237
|
+
|
|
238
|
+
// Built-in Agent Demotion (default: true)
|
|
239
|
+
"demote_builtin": true,
|
|
313
240
|
|
|
314
241
|
// 並列制御
|
|
315
242
|
"background_task": {
|
|
316
243
|
"defaultConcurrency": 3,
|
|
317
|
-
"providerConcurrency": {
|
|
318
|
-
|
|
319
|
-
"zai-coding-plan": 10
|
|
320
|
-
},
|
|
321
|
-
"modelConcurrency": {
|
|
322
|
-
"openai/gpt-5.2": 2
|
|
323
|
-
}
|
|
244
|
+
"providerConcurrency": { "openai": 8 },
|
|
245
|
+
"modelConcurrency": { "openai/gpt-5.2": 2 }
|
|
324
246
|
},
|
|
325
247
|
|
|
326
|
-
//
|
|
327
|
-
"
|
|
328
|
-
"maxRatio": 0.3,
|
|
329
|
-
"slopThreshold": 3
|
|
330
|
-
},
|
|
248
|
+
// Token Truncation
|
|
249
|
+
"token_truncation": { "maxChars": 30000 },
|
|
331
250
|
|
|
332
|
-
//
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
|
|
251
|
+
// Comment Checker
|
|
252
|
+
"comment_checker": { "maxRatio": 0.3, "slopThreshold": 3 },
|
|
253
|
+
|
|
254
|
+
// Todo Enforcer
|
|
255
|
+
"todo_enforcer": { "maxEnforcements": 5 },
|
|
336
256
|
|
|
337
257
|
// MCP API キー
|
|
338
|
-
"mcp_api_keys": {
|
|
339
|
-
"context7": "ctx7sk-..."
|
|
340
|
-
}
|
|
258
|
+
"mcp_api_keys": { "context7": "ctx7sk-..." }
|
|
341
259
|
}
|
|
342
260
|
```
|
|
343
261
|
|
|
344
|
-
### エージェントオーバーライドで使えるフィールド
|
|
345
|
-
|
|
346
|
-
| フィールド | 型 | 説明 |
|
|
347
|
-
|-----------|---|------|
|
|
348
|
-
| `model` | string | 使用モデル (provider/model 形式) |
|
|
349
|
-
| `description` | string | エージェントの説明 |
|
|
350
|
-
| `prompt` | string | システムプロンプト全体を差し替え |
|
|
351
|
-
| `prompt_append` | string | プロンプト末尾に追記 |
|
|
352
|
-
| `disable` | boolean | 個別に無効化 |
|
|
353
|
-
| `mode` | `"subagent"` / `"primary"` / `"all"` | 実行モード |
|
|
354
|
-
| `maxTokens` | number | 最大出力トークン |
|
|
355
|
-
| `thinking` | `{ type, budgetTokens }` | 思考トークン設定 |
|
|
356
|
-
| `reasoningEffort` | `"low"` / `"medium"` / `"high"` / `"xhigh"` | 推論レベル |
|
|
357
|
-
| `temperature` | number (0-2) | サンプリング温度 |
|
|
358
|
-
| `top_p` | number (0-1) | Top-p |
|
|
359
|
-
| `category` | string | デフォルトカテゴリ |
|
|
360
|
-
| `variant` | string | モデルバリアント |
|
|
361
|
-
|
|
362
|
-
## フック再帰防止
|
|
363
|
-
|
|
364
|
-
spawn_agent / ralph_loop で生成されたエフェメラルセッションは `internalSessions` Set で追跡される。`chat.message`、`system.transform`、`tool.execute.after`、`event` の各フックは内部セッションに対してスキップされ、キーワード検知・ルール注入・Comment Checker・Todo Enforcer がサブエージェントに二重適用されることを防ぐ。
|
|
365
|
-
|
|
366
262
|
## プロジェクト構成
|
|
367
263
|
|
|
368
264
|
```
|
|
369
265
|
opencode-ultra/
|
|
370
266
|
├── src/
|
|
371
|
-
│ ├── index.ts
|
|
372
|
-
│ ├── config.ts
|
|
267
|
+
│ ├── index.ts # プラグインエントリポイント
|
|
268
|
+
│ ├── config.ts # 設定ロード・マージ・バリデーション
|
|
373
269
|
│ ├── agents/
|
|
374
|
-
│ │ ├── types.ts
|
|
375
|
-
│ │ └── index.ts
|
|
270
|
+
│ │ ├── types.ts # AgentDef 型定義
|
|
271
|
+
│ │ └── index.ts # ビルトインエージェント + 動的プロンプト生成
|
|
376
272
|
│ ├── hooks/
|
|
377
|
-
│ │ ├── keyword-detector.ts
|
|
378
|
-
│ │ ├── rules-injector.ts
|
|
379
|
-
│ │ ├──
|
|
380
|
-
│ │
|
|
273
|
+
│ │ ├── keyword-detector.ts # ultrawork/search/analyze/think 検知
|
|
274
|
+
│ │ ├── rules-injector.ts # rules.md + ARCHITECTURE.md + CODE_STYLE.md 注入
|
|
275
|
+
│ │ ├── fragment-injector.ts # エージェント毎の断片注入
|
|
276
|
+
│ │ ├── prompt-renderer.ts # モデル別フォーマット変換
|
|
277
|
+
│ │ ├── comment-checker.ts # AI スロップコメント検知
|
|
278
|
+
│ │ ├── token-truncation.ts # ツール出力の賢い圧縮
|
|
279
|
+
│ │ ├── todo-enforcer.ts # 未完了 TODO 強制継続
|
|
280
|
+
│ │ └── session-compaction.ts # セッション圧縮サマリ生成
|
|
381
281
|
│ ├── tools/
|
|
382
|
-
│ │ ├── spawn-agent.ts
|
|
383
|
-
│ │
|
|
384
|
-
│ ├──
|
|
385
|
-
│ │ ├──
|
|
386
|
-
│ │
|
|
387
|
-
│
|
|
388
|
-
│ ├── categories/
|
|
389
|
-
│
|
|
390
|
-
│
|
|
391
|
-
|
|
392
|
-
│ │ └── index.ts # MCP 登録ロジック
|
|
393
|
-
│ └── shared/
|
|
394
|
-
│ ├── paths.ts # XDG パス解決
|
|
395
|
-
│ ├── jsonc.ts # JSONC パーサー
|
|
396
|
-
│ ├── log.ts # [opencode-ultra] プレフィックスログ
|
|
397
|
-
│ └── index.ts # バレルエクスポート
|
|
398
|
-
├── __test__/ # Bun テスト (70 tests)
|
|
282
|
+
│ │ ├── spawn-agent.ts # 並列エージェント実行
|
|
283
|
+
│ │ ├── ralph-loop.ts # 自律ループ実行
|
|
284
|
+
│ │ ├── batch-read.ts # 複数ファイル並列読み込み
|
|
285
|
+
│ │ ├── continuity-ledger.ts # セッション間文脈継続
|
|
286
|
+
│ │ └── ast-search.ts # AST-aware コード検索
|
|
287
|
+
│ ├── concurrency/ # Semaphore + ConcurrencyPool
|
|
288
|
+
│ ├── categories/ # ビルトインカテゴリ
|
|
289
|
+
│ ├── mcp/ # MCP サーバー登録
|
|
290
|
+
│ └── shared/ # ユーティリティ
|
|
291
|
+
├── __test__/ # Bun テスト (92 tests, 20 files)
|
|
399
292
|
├── package.json
|
|
400
293
|
├── tsconfig.json
|
|
401
294
|
└── .gitignore
|
|
@@ -404,13 +297,8 @@ opencode-ultra/
|
|
|
404
297
|
## ビルド・テスト
|
|
405
298
|
|
|
406
299
|
```bash
|
|
407
|
-
# 依存インストール
|
|
408
300
|
bun install
|
|
409
|
-
|
|
410
|
-
# ビルド
|
|
411
|
-
bun build src/index.ts --outdir dist --target bun --format esm
|
|
412
|
-
|
|
413
|
-
# テスト
|
|
301
|
+
bun run build
|
|
414
302
|
bun test
|
|
415
303
|
```
|
|
416
304
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import type { ModelRef } from "../shared/types";
|
|
1
2
|
export type PromptFormat = "markdown" | "xml" | "json";
|
|
2
3
|
export type PromptRendererConfig = {
|
|
3
4
|
default: PromptFormat;
|
|
4
5
|
model_overrides: Record<string, PromptFormat>;
|
|
5
6
|
};
|
|
6
|
-
export declare function resolvePromptFormat(inputModel:
|
|
7
|
+
export declare function resolvePromptFormat(inputModel: ModelRef | undefined, config?: Partial<PromptRendererConfig>): PromptFormat;
|
|
7
8
|
export declare function formatSystemSection(format: PromptFormat, content: string): string;
|
|
8
9
|
export declare function createPromptRendererHook(internalSessions: Set<string>, config?: Partial<PromptRendererConfig>): (input: {
|
|
9
10
|
sessionID?: string;
|
|
10
|
-
model?:
|
|
11
|
+
model?: ModelRef;
|
|
11
12
|
}, output: {
|
|
12
13
|
system: string[];
|
|
13
14
|
}) => void;
|
|
@@ -11,7 +11,7 @@ type MessageLike = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function buildSessionSummary(messages: MessageLike[]): string;
|
|
13
13
|
export declare function createSessionCompactionHook(ctx: {
|
|
14
|
-
client:
|
|
14
|
+
client: Pick<import("../shared/types").ExtendedClient, "session">;
|
|
15
15
|
}, internalSessions: Set<string>): (input: {
|
|
16
16
|
sessionID: string;
|
|
17
17
|
}, output: {
|
package/dist/index.js
CHANGED
|
@@ -14369,6 +14369,49 @@ function log(message, data) {
|
|
|
14369
14369
|
console.error(`${PREFIX} ${message}`);
|
|
14370
14370
|
}
|
|
14371
14371
|
}
|
|
14372
|
+
// src/shared/ttl-map.ts
|
|
14373
|
+
class TtlMap {
|
|
14374
|
+
map = new Map;
|
|
14375
|
+
maxSize;
|
|
14376
|
+
ttlMs;
|
|
14377
|
+
constructor(opts = {}) {
|
|
14378
|
+
this.maxSize = opts.maxSize ?? 1000;
|
|
14379
|
+
this.ttlMs = opts.ttlMs ?? 0;
|
|
14380
|
+
}
|
|
14381
|
+
get(key) {
|
|
14382
|
+
const entry = this.map.get(key);
|
|
14383
|
+
if (!entry)
|
|
14384
|
+
return;
|
|
14385
|
+
if (this.ttlMs > 0 && Date.now() - entry.createdAt > this.ttlMs) {
|
|
14386
|
+
this.map.delete(key);
|
|
14387
|
+
return;
|
|
14388
|
+
}
|
|
14389
|
+
return entry.value;
|
|
14390
|
+
}
|
|
14391
|
+
set(key, value) {
|
|
14392
|
+
if (this.map.has(key)) {
|
|
14393
|
+
this.map.delete(key);
|
|
14394
|
+
}
|
|
14395
|
+
if (this.map.size >= this.maxSize) {
|
|
14396
|
+
const oldest = this.map.keys().next().value;
|
|
14397
|
+
if (oldest !== undefined)
|
|
14398
|
+
this.map.delete(oldest);
|
|
14399
|
+
}
|
|
14400
|
+
this.map.set(key, { value, createdAt: Date.now() });
|
|
14401
|
+
}
|
|
14402
|
+
has(key) {
|
|
14403
|
+
return this.get(key) !== undefined;
|
|
14404
|
+
}
|
|
14405
|
+
delete(key) {
|
|
14406
|
+
return this.map.delete(key);
|
|
14407
|
+
}
|
|
14408
|
+
get size() {
|
|
14409
|
+
return this.map.size;
|
|
14410
|
+
}
|
|
14411
|
+
clear() {
|
|
14412
|
+
this.map.clear();
|
|
14413
|
+
}
|
|
14414
|
+
}
|
|
14372
14415
|
// src/config.ts
|
|
14373
14416
|
var AgentOverrideSchema = exports_external.object({
|
|
14374
14417
|
model: exports_external.string().optional(),
|
|
@@ -14590,6 +14633,40 @@ var BUILTIN_AGENTS = {
|
|
|
14590
14633
|
mode: "subagent",
|
|
14591
14634
|
reasoningEffort: "medium",
|
|
14592
14635
|
maxTokens: 64000
|
|
14636
|
+
},
|
|
14637
|
+
scout: {
|
|
14638
|
+
model: "anthropic/claude-sonnet-4-5",
|
|
14639
|
+
description: "Plugin ecosystem researcher \u2014 finds, analyzes, and compares OpenCode plugins for self-improvement",
|
|
14640
|
+
prompt: `You are Scout, an OpenCode plugin ecosystem researcher.
|
|
14641
|
+
|
|
14642
|
+
## YOUR MISSION
|
|
14643
|
+
Search the web (npm, GitHub, OpenCode community) for OpenCode plugins and extensions.
|
|
14644
|
+
Analyze their features, architecture, and quality. Compare with opencode-ultra.
|
|
14645
|
+
|
|
14646
|
+
## SEARCH STRATEGY
|
|
14647
|
+
1. Search npm for "opencode-plugin", "opencode-ai", "@opencode" packages
|
|
14648
|
+
2. Search GitHub for "opencode plugin", "opencode extension", "oh-my-opencode"
|
|
14649
|
+
3. Look at package.json dependencies on @opencode-ai/plugin or @opencode-ai/sdk
|
|
14650
|
+
4. Read README files and source code of discovered plugins
|
|
14651
|
+
|
|
14652
|
+
## OUTPUT FORMAT
|
|
14653
|
+
For each plugin found, report:
|
|
14654
|
+
- **Name**: package name + repo URL
|
|
14655
|
+
- **Version**: latest version
|
|
14656
|
+
- **Features**: bullet list of capabilities
|
|
14657
|
+
- **Architecture**: hook types used, tool count, agent count
|
|
14658
|
+
- **Quality signals**: test count, TypeScript, last updated, download count
|
|
14659
|
+
- **Unique ideas**: features that opencode-ultra does NOT have
|
|
14660
|
+
|
|
14661
|
+
## COMPARISON
|
|
14662
|
+
After listing plugins, generate a structured gap analysis:
|
|
14663
|
+
- Features others have that opencode-ultra lacks
|
|
14664
|
+
- Features opencode-ultra has that others lack (competitive advantages)
|
|
14665
|
+
- Improvement priority list (high/medium/low impact)
|
|
14666
|
+
|
|
14667
|
+
Be thorough but focused. Skip abandoned or trivial plugins.`,
|
|
14668
|
+
mode: "subagent",
|
|
14669
|
+
maxTokens: 32000
|
|
14593
14670
|
}
|
|
14594
14671
|
};
|
|
14595
14672
|
function buildAgentTable(agents) {
|
|
@@ -14720,6 +14797,7 @@ var ULTRAWORK_PATTERN = /\b(ultrawork|ulw)\b/i;
|
|
|
14720
14797
|
var THINK_PATTERN = /\b(think\s+hard|think\s+through|think\s+deeply|think\s+carefully)\b|\u3058\u3063\u304F\u308A|\u6DF1\u304F\u8003\u3048\u3066|\u719F\u8003/i;
|
|
14721
14798
|
var SEARCH_PATTERN = /\b(search|find|locate|lookup|look\s*up|explore|discover|scan|grep|query|browse|detect|trace|seek|track|pinpoint|hunt)\b|where\s+is|show\s+me|list\s+all|\u691C\u7D22|\u63A2\u3057\u3066|\u898B\u3064\u3051\u3066|\u30B5\u30FC\u30C1|\u63A2\u7D22|\u30B9\u30AD\u30E3\u30F3|\u3069\u3053|\u767A\u898B|\u635C\u7D22|\u898B\u3064\u3051\u51FA\u3059|\u4E00\u89A7|\u641C\u7D22|\u67E5\u627E|\u5BFB\u627E|\u67E5\u8BE2|\u68C0\u7D22|\u5B9A\u4F4D|\u626B\u63CF|\u53D1\u73B0|\u5728\u54EA\u91CC|\u627E\u51FA\u6765|\u5217\u51FA/i;
|
|
14722
14799
|
var ANALYZE_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|\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/i;
|
|
14800
|
+
var EVOLVE_PATTERN = /\b(evolve|self[\s-]?improve|self[\s-]?upgrade|plugin[\s-]?scout|ecosystem[\s-]?scan)\b|\u81EA\u5DF1\u6539\u5584|\u9032\u5316|\u30D7\u30E9\u30B0\u30A4\u30F3\u63A2\u7D22|\u30A8\u30B3\u30B7\u30B9\u30C6\u30E0|\u81EA\u6211\u8FDB\u5316|\u63D2\u4EF6\u641C\u7D22/i;
|
|
14723
14801
|
function removeCodeBlocks(text) {
|
|
14724
14802
|
return text.replace(CODE_BLOCK_PATTERN, "").replace(INLINE_CODE_PATTERN, "");
|
|
14725
14803
|
}
|
|
@@ -14730,7 +14808,8 @@ var KEYWORD_DEFS = [
|
|
|
14730
14808
|
{ pattern: ULTRAWORK_PATTERN, type: "ultrawork", getMessage: () => ULTRAWORK_MESSAGE },
|
|
14731
14809
|
{ pattern: SEARCH_PATTERN, type: "search", getMessage: () => SEARCH_MESSAGE },
|
|
14732
14810
|
{ pattern: ANALYZE_PATTERN, type: "analyze", getMessage: () => ANALYZE_MESSAGE },
|
|
14733
|
-
{ pattern: THINK_PATTERN, type: "think", getMessage: () => THINK_MESSAGE }
|
|
14811
|
+
{ pattern: THINK_PATTERN, type: "think", getMessage: () => THINK_MESSAGE },
|
|
14812
|
+
{ pattern: EVOLVE_PATTERN, type: "evolve", getMessage: () => EVOLVE_MESSAGE }
|
|
14734
14813
|
];
|
|
14735
14814
|
function detectKeywords(text) {
|
|
14736
14815
|
const clean = removeCodeBlocks(text);
|
|
@@ -14820,11 +14899,38 @@ IF COMPLEX \u2014 DO NOT STRUGGLE ALONE. Consult specialists:
|
|
|
14820
14899
|
|
|
14821
14900
|
SYNTHESIZE findings before proceeding.`;
|
|
14822
14901
|
var THINK_MESSAGE = `Extended thinking enabled. Take your time to reason thoroughly.`;
|
|
14902
|
+
var EVOLVE_MESSAGE = `[evolve-mode] SELF-IMPROVEMENT CYCLE ACTIVATED.
|
|
14903
|
+
|
|
14904
|
+
## MISSION
|
|
14905
|
+
Search the OpenCode plugin ecosystem, find what others have built, and identify gaps in opencode-ultra.
|
|
14906
|
+
|
|
14907
|
+
## STEPS
|
|
14908
|
+
1. **SCOUT** \u2014 Spawn the **scout** agent to search npm/GitHub for OpenCode plugins
|
|
14909
|
+
2. **READ SELF** \u2014 Read opencode-ultra's own README.md and key source files to know current capabilities
|
|
14910
|
+
3. **COMPARE** \u2014 Generate a structured gap analysis (what we have vs what we're missing)
|
|
14911
|
+
4. **PRIORITIZE** \u2014 Rank missing features by impact (high/medium/low)
|
|
14912
|
+
5. **PROPOSE** \u2014 Present improvement plan to the user
|
|
14913
|
+
6. **SAVE** \u2014 Save findings to a continuity ledger via ledger_save for future reference
|
|
14914
|
+
|
|
14915
|
+
## EXECUTION
|
|
14916
|
+
\`\`\`
|
|
14917
|
+
spawn_agent({
|
|
14918
|
+
agents: [
|
|
14919
|
+
{agent: "scout", prompt: "Search npm and GitHub for OpenCode 1.2.x plugins. Find all published plugins, analyze features, compare with opencode-ultra.", description: "Plugin ecosystem scan"},
|
|
14920
|
+
{agent: "explore", prompt: "Read opencode-ultra's README.md, src/index.ts, src/agents/index.ts to catalog current features", description: "Self-analysis"}
|
|
14921
|
+
]
|
|
14922
|
+
})
|
|
14923
|
+
\`\`\`
|
|
14924
|
+
|
|
14925
|
+
After gathering results, synthesize into a gap analysis and present to the user.
|
|
14926
|
+
Save the analysis via: ledger_save({name: "evolve-scan-YYYY-MM-DD", content: "..."})
|
|
14927
|
+
|
|
14928
|
+
**This is how opencode-ultra gets better \u2014 by knowing what it doesn't know.**`;
|
|
14823
14929
|
|
|
14824
14930
|
// src/hooks/rules-injector.ts
|
|
14825
14931
|
import * as fs2 from "fs";
|
|
14826
14932
|
import * as path2 from "path";
|
|
14827
|
-
var cache = new
|
|
14933
|
+
var cache = new TtlMap({ maxSize: 50, ttlMs: 10 * 60 * 1000 });
|
|
14828
14934
|
function loadCachedFile(filePath) {
|
|
14829
14935
|
try {
|
|
14830
14936
|
if (!fs2.existsSync(filePath))
|
|
@@ -14882,7 +14988,7 @@ function loadCodeStyle(projectDir) {
|
|
|
14882
14988
|
// src/hooks/fragment-injector.ts
|
|
14883
14989
|
import * as fs3 from "fs";
|
|
14884
14990
|
import * as path3 from "path";
|
|
14885
|
-
var cache2 = new
|
|
14991
|
+
var cache2 = new TtlMap({ maxSize: 100, ttlMs: 10 * 60 * 1000 });
|
|
14886
14992
|
async function readCached(absPath) {
|
|
14887
14993
|
try {
|
|
14888
14994
|
const st = await fs3.promises.stat(absPath);
|
|
@@ -27322,7 +27428,8 @@ function resolveCategory(categoryName, configCategories) {
|
|
|
27322
27428
|
|
|
27323
27429
|
// src/tools/spawn-agent.ts
|
|
27324
27430
|
function showToast(ctx, title, message, variant = "info") {
|
|
27325
|
-
ctx.client
|
|
27431
|
+
const client = ctx.client;
|
|
27432
|
+
client.tui?.showToast?.({
|
|
27326
27433
|
body: { title, message, variant, duration: 2000 }
|
|
27327
27434
|
})?.catch?.(() => {});
|
|
27328
27435
|
}
|
|
@@ -27427,6 +27534,18 @@ spawn_agent({
|
|
|
27427
27534
|
if (!agents || agents.length === 0) {
|
|
27428
27535
|
return "No agents specified.";
|
|
27429
27536
|
}
|
|
27537
|
+
for (let i = 0;i < agents.length; i++) {
|
|
27538
|
+
const t = agents[i];
|
|
27539
|
+
if (!t.agent || typeof t.agent !== "string" || t.agent.trim().length === 0) {
|
|
27540
|
+
return `Error: agents[${i}].agent is required (non-empty string)`;
|
|
27541
|
+
}
|
|
27542
|
+
if (!t.prompt || typeof t.prompt !== "string" || t.prompt.trim().length === 0) {
|
|
27543
|
+
return `Error: agents[${i}].prompt is required (non-empty string)`;
|
|
27544
|
+
}
|
|
27545
|
+
if (!t.description || typeof t.description !== "string") {
|
|
27546
|
+
return `Error: agents[${i}].description is required`;
|
|
27547
|
+
}
|
|
27548
|
+
}
|
|
27430
27549
|
const agentNames = agents.map((a) => a.agent);
|
|
27431
27550
|
log("spawn_agent", { count: agents.length, agents: agentNames });
|
|
27432
27551
|
showToast(ctx, "spawn_agent", `${agents.length} agents: ${agentNames.join(", ")}`);
|
|
@@ -28190,6 +28309,8 @@ class Semaphore {
|
|
|
28190
28309
|
});
|
|
28191
28310
|
}
|
|
28192
28311
|
release() {
|
|
28312
|
+
if (this.active <= 0)
|
|
28313
|
+
return;
|
|
28193
28314
|
this.active--;
|
|
28194
28315
|
const next = this.queue.shift();
|
|
28195
28316
|
if (next)
|
|
@@ -28279,7 +28400,8 @@ async function registerMcps(ctx, disabled, apiKeys) {
|
|
|
28279
28400
|
if (config3.env) {
|
|
28280
28401
|
body.env = config3.env;
|
|
28281
28402
|
}
|
|
28282
|
-
|
|
28403
|
+
const client = ctx.client;
|
|
28404
|
+
await client.mcp?.add?.({ body });
|
|
28283
28405
|
log(`MCP ${name} registered${apiKey ? " (with API key)" : ""}`);
|
|
28284
28406
|
} catch (err) {
|
|
28285
28407
|
log(`MCP ${name} registration failed: ${err}`);
|
|
@@ -28313,10 +28435,10 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
28313
28435
|
const todoEnforcer = createTodoEnforcer(ctx, internalSessions, pluginConfig.todo_enforcer?.maxEnforcements);
|
|
28314
28436
|
const commentCheckerHook = createCommentCheckerHook(internalSessions, pluginConfig.comment_checker?.maxRatio, pluginConfig.comment_checker?.slopThreshold);
|
|
28315
28437
|
const tokenTruncationHook = createTokenTruncationHook(internalSessions, pluginConfig.token_truncation?.maxChars);
|
|
28316
|
-
const sessionCompactionHook = createSessionCompactionHook(ctx, internalSessions);
|
|
28438
|
+
const sessionCompactionHook = createSessionCompactionHook({ client: ctx.client }, internalSessions);
|
|
28317
28439
|
const fragmentInjector = createFragmentInjector(ctx, internalSessions, pluginConfig.fragments);
|
|
28318
28440
|
const promptRendererHook = createPromptRendererHook(internalSessions, pluginConfig.prompt_renderer);
|
|
28319
|
-
const pendingKeywords = new
|
|
28441
|
+
const pendingKeywords = new TtlMap({ maxSize: 200, ttlMs: 5 * 60 * 1000 });
|
|
28320
28442
|
log("Config loaded", {
|
|
28321
28443
|
agentCount: Object.keys(agents).length,
|
|
28322
28444
|
disabledHooks: [...disabledHooks],
|
|
@@ -28397,7 +28519,8 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
28397
28519
|
if (output.message.variant === undefined) {
|
|
28398
28520
|
output.message.variant = "max";
|
|
28399
28521
|
}
|
|
28400
|
-
ctx.client
|
|
28522
|
+
const client = ctx.client;
|
|
28523
|
+
client.tui?.showToast?.({
|
|
28401
28524
|
body: {
|
|
28402
28525
|
title: "ULTRAWORK MODE",
|
|
28403
28526
|
message: "Maximum precision engaged. All agents at your disposal.",
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Map with max size eviction (LRU-like: evicts oldest entry when full).
|
|
3
|
+
* Optionally supports TTL-based expiry.
|
|
4
|
+
*/
|
|
5
|
+
export declare class TtlMap<K, V> {
|
|
6
|
+
private map;
|
|
7
|
+
private maxSize;
|
|
8
|
+
private ttlMs;
|
|
9
|
+
constructor(opts?: {
|
|
10
|
+
maxSize?: number;
|
|
11
|
+
ttlMs?: number;
|
|
12
|
+
});
|
|
13
|
+
get(key: K): V | undefined;
|
|
14
|
+
set(key: K, value: V): void;
|
|
15
|
+
has(key: K): boolean;
|
|
16
|
+
delete(key: K): boolean;
|
|
17
|
+
get size(): number;
|
|
18
|
+
clear(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended type definitions for OpenCode client APIs that are not (yet) in @opencode-ai/sdk types.
|
|
3
|
+
* These describe the actual runtime shape observed in OpenCode 1.2.x.
|
|
4
|
+
*/
|
|
5
|
+
/** Model identifier as passed through hook input */
|
|
6
|
+
export interface ModelRef {
|
|
7
|
+
providerID?: string;
|
|
8
|
+
modelID?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Toast notification (TUI-only, non-standard) */
|
|
11
|
+
export interface ToastOptions {
|
|
12
|
+
body: {
|
|
13
|
+
title: string;
|
|
14
|
+
message: string;
|
|
15
|
+
variant?: "info" | "success" | "warning" | "error";
|
|
16
|
+
duration?: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Extended OpenCode client with optional TUI and MCP methods */
|
|
20
|
+
export interface ExtendedClient {
|
|
21
|
+
session: {
|
|
22
|
+
create: (opts: {
|
|
23
|
+
body: Record<string, unknown>;
|
|
24
|
+
query?: {
|
|
25
|
+
directory?: string;
|
|
26
|
+
};
|
|
27
|
+
}) => Promise<{
|
|
28
|
+
data?: {
|
|
29
|
+
id?: string;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
prompt: (opts: {
|
|
33
|
+
path: {
|
|
34
|
+
id: string;
|
|
35
|
+
};
|
|
36
|
+
body: {
|
|
37
|
+
parts: Array<{
|
|
38
|
+
type: string;
|
|
39
|
+
text: string;
|
|
40
|
+
}>;
|
|
41
|
+
agent?: string;
|
|
42
|
+
};
|
|
43
|
+
query?: {
|
|
44
|
+
directory?: string;
|
|
45
|
+
};
|
|
46
|
+
}) => Promise<unknown>;
|
|
47
|
+
messages: (opts: {
|
|
48
|
+
path: {
|
|
49
|
+
id: string;
|
|
50
|
+
};
|
|
51
|
+
query?: {
|
|
52
|
+
directory?: string;
|
|
53
|
+
};
|
|
54
|
+
}) => Promise<{
|
|
55
|
+
data?: Array<{
|
|
56
|
+
info?: {
|
|
57
|
+
role?: string;
|
|
58
|
+
};
|
|
59
|
+
parts?: Array<{
|
|
60
|
+
type: string;
|
|
61
|
+
text?: string;
|
|
62
|
+
}>;
|
|
63
|
+
}>;
|
|
64
|
+
}>;
|
|
65
|
+
delete: (opts: {
|
|
66
|
+
path: {
|
|
67
|
+
id: string;
|
|
68
|
+
};
|
|
69
|
+
query?: {
|
|
70
|
+
directory?: string;
|
|
71
|
+
};
|
|
72
|
+
}) => Promise<unknown>;
|
|
73
|
+
listMessages?: (opts: {
|
|
74
|
+
id: string;
|
|
75
|
+
}) => Promise<{
|
|
76
|
+
messages?: unknown[];
|
|
77
|
+
} | unknown[]>;
|
|
78
|
+
};
|
|
79
|
+
tui?: {
|
|
80
|
+
showToast?: (opts: ToastOptions) => Promise<void>;
|
|
81
|
+
};
|
|
82
|
+
mcp?: {
|
|
83
|
+
add?: (opts: {
|
|
84
|
+
body: Record<string, unknown>;
|
|
85
|
+
}) => Promise<void>;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** System transform hook input */
|
|
89
|
+
export interface SystemTransformInput {
|
|
90
|
+
sessionID?: string;
|
|
91
|
+
model?: ModelRef;
|
|
92
|
+
agent?: string;
|
|
93
|
+
}
|
package/package.json
CHANGED