opencode-ultra 0.5.1 → 0.6.1
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 +74 -15
- package/dist/index.js +464 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
# opencode-ultra
|
|
2
2
|
|
|
3
3
|
[oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) をベースに、micode / opencode-skillful の良い部分を取り込んだ OpenCode 1.2.x プラグイン。
|
|
4
|
-
マルチエージェントオーケストレーション・キーワード駆動モード切替・ルール注入・セッション継続・AST
|
|
4
|
+
マルチエージェントオーケストレーション・キーワード駆動モード切替・ルール注入・セッション継続・AST検索・自己改善 (evolve) を軽量な単一プラグインで実現する。
|
|
5
5
|
|
|
6
6
|
## 機能一覧
|
|
7
7
|
|
|
8
|
-
### ツール (
|
|
8
|
+
### ツール (8)
|
|
9
9
|
|
|
10
10
|
| ツール | 説明 |
|
|
11
11
|
|--------|------|
|
|
12
|
-
| `spawn_agent` | 並列エージェント実行 (ConcurrencyPool
|
|
13
|
-
| `ralph_loop` | 自律ループ実行 (`<promise>DONE</promise>`
|
|
12
|
+
| `spawn_agent` | 並列エージェント実行 (ConcurrencyPool + spawn limit + timeout + sanitizer) |
|
|
13
|
+
| `ralph_loop` | 自律ループ実行 (`<promise>DONE</promise>` で完了検知, per-iteration timeout) |
|
|
14
14
|
| `cancel_ralph` | 実行中の Ralph Loop をキャンセル |
|
|
15
15
|
| `batch_read` | 複数ファイル並列読み込み (最大20) |
|
|
16
16
|
| `ledger_save` | Continuity Ledger の保存 (.opencode/ledgers/) |
|
|
17
17
|
| `ledger_load` | Continuity Ledger の読み込み (名前指定 or 最新) |
|
|
18
18
|
| `ast_search` | AST-aware コード検索 (ast-grep/sg バイナリ使用、未インストール時は自動スキップ) |
|
|
19
|
+
| `evolve_apply` | プラグイン推薦の信頼度スコア評価 + OpenCode 設定への自動適用 (dry-run/backup 対応) |
|
|
19
20
|
|
|
20
21
|
### フック (9)
|
|
21
22
|
|
|
22
23
|
| フック | hook ポイント | 説明 |
|
|
23
24
|
|--------|-------------|------|
|
|
24
|
-
| `keyword-detector` | chat.message | ultrawork/search/analyze/think キーワード検知 |
|
|
25
|
+
| `keyword-detector` | chat.message | ultrawork/search/analyze/think/evolve キーワード検知 |
|
|
25
26
|
| `rules-injector` | system.transform | .opencode/rules.md を system prompt に注入 |
|
|
26
27
|
| `context-injector` | system.transform | ARCHITECTURE.md / CODE_STYLE.md を自動注入 |
|
|
27
28
|
| `fragment-injector` | system.transform | エージェント毎のカスタムプロンプト断片注入 |
|
|
@@ -31,6 +32,45 @@
|
|
|
31
32
|
| `todo-enforcer` | event (session.idle) | 未完了 TODO を検知して強制継続 |
|
|
32
33
|
| `session-compaction` | experimental.session.compacting | セッション圧縮時に structured summary 生成 |
|
|
33
34
|
|
|
35
|
+
### Safety
|
|
36
|
+
|
|
37
|
+
| 機能 | 説明 |
|
|
38
|
+
|------|------|
|
|
39
|
+
| **Prompt Injection Sanitizer** | エージェント出力から注入パターンを検知・無害化 (17パターン, 6カテゴリ) |
|
|
40
|
+
| **Spawn Limit** | 同時実行セッション上限 (default: 15) — エージェント爆発防止 |
|
|
41
|
+
| **Agent Timeout** | per-agent タイムアウト (default: 180s) — ハング防止 |
|
|
42
|
+
| **Trust Score** | npm パッケージの信頼度スコア (0-100) — evolve 推薦の品質ゲート |
|
|
43
|
+
|
|
44
|
+
### Evolve (自己改善)
|
|
45
|
+
|
|
46
|
+
| キーワード | 効果 |
|
|
47
|
+
|-----------|------|
|
|
48
|
+
| `evolve` / `self-improve` / `自己改善` / `進化` | evolve モード起動 |
|
|
49
|
+
|
|
50
|
+
evolve モードでは:
|
|
51
|
+
1. **scout** エージェントが npm/GitHub を検索し、プラグインのメタデータを収集
|
|
52
|
+
2. **explore** エージェントが opencode-ultra 自身の機能をカタログ化
|
|
53
|
+
3. 発見されたプラグインに **Trust Score** (0-100) を自動計算
|
|
54
|
+
4. ギャップ分析と改善提案を生成
|
|
55
|
+
5. ユーザー承認後、`evolve_apply` で設定に自動適用 (バックアップ付き)
|
|
56
|
+
|
|
57
|
+
Trust Score 評価基準:
|
|
58
|
+
|
|
59
|
+
| カテゴリ | 配点 | チェック項目 |
|
|
60
|
+
|----------|------|-------------|
|
|
61
|
+
| Recency | 25 | 最終 publish からの日数 |
|
|
62
|
+
| Popularity | 25 | 週間 DL 数 + GitHub stars |
|
|
63
|
+
| Quality | 20 | ライセンス, README, メンテナ数, description |
|
|
64
|
+
| Repository | 15 | GitHub/GitLab リポジトリの存在 |
|
|
65
|
+
| Safety | 15 | typosquat 検知, 依存数, 既知スコープ |
|
|
66
|
+
|
|
67
|
+
| スコア | レベル | 自動インストール |
|
|
68
|
+
|--------|--------|----------------|
|
|
69
|
+
| 90-100 | HIGH | 可 |
|
|
70
|
+
| 70-89 | MEDIUM | レビュー推奨 |
|
|
71
|
+
| 40-69 | LOW | 要注意 |
|
|
72
|
+
| 0-39 | RISKY | ブロック |
|
|
73
|
+
|
|
34
74
|
### その他
|
|
35
75
|
|
|
36
76
|
| 機能 | 説明 |
|
|
@@ -46,7 +86,7 @@
|
|
|
46
86
|
```
|
|
47
87
|
User
|
|
48
88
|
│
|
|
49
|
-
│ "ultrawork" / "ulw" / "think hard" キーワード
|
|
89
|
+
│ "ultrawork" / "ulw" / "evolve" / "think hard" キーワード
|
|
50
90
|
│
|
|
51
91
|
▼
|
|
52
92
|
┌──────────────────────────────────────────────┐
|
|
@@ -54,7 +94,7 @@ User
|
|
|
54
94
|
│ │
|
|
55
95
|
│ chat.message hook │
|
|
56
96
|
│ ├─ keyword-detector │
|
|
57
|
-
│ │ └─ ultrawork
|
|
97
|
+
│ │ └─ ultrawork/search/analyze/think/evolve │
|
|
58
98
|
│ └─ ultrawork → variant: "max" │
|
|
59
99
|
│ │
|
|
60
100
|
│ system.transform hook │
|
|
@@ -80,11 +120,20 @@ User
|
|
|
80
120
|
│ └─ demote built-in agents to subagent │
|
|
81
121
|
│ │
|
|
82
122
|
│ tools │
|
|
83
|
-
│ ├─ spawn_agent (並列実行 +
|
|
123
|
+
│ ├─ spawn_agent (並列実行 + 安全制御) │
|
|
124
|
+
│ │ ├─ ConcurrencyPool │
|
|
125
|
+
│ │ ├─ Spawn limit (max 15) │
|
|
126
|
+
│ │ ├─ Agent timeout (180s) │
|
|
127
|
+
│ │ └─ Prompt injection sanitizer │
|
|
84
128
|
│ ├─ ralph_loop / cancel_ralph │
|
|
85
129
|
│ ├─ batch_read (複数ファイル並列読み込み) │
|
|
86
130
|
│ ├─ ledger_save / ledger_load (文脈継続) │
|
|
87
|
-
│
|
|
131
|
+
│ ├─ ast_search (構文木検索, optional) │
|
|
132
|
+
│ └─ evolve_apply (信頼度スコア + 設定適用) │
|
|
133
|
+
│ │
|
|
134
|
+
│ safety │
|
|
135
|
+
│ ├─ sanitizer (17 injection patterns) │
|
|
136
|
+
│ └─ trust-score (5-factor, 0-100) │
|
|
88
137
|
│ │
|
|
89
138
|
│ MCP registration │
|
|
90
139
|
│ └─ context7 (自動登録) │
|
|
@@ -119,6 +168,7 @@ Sisyphus (オーケストレーター) が直接コードを読み、実装は
|
|
|
119
168
|
| **momus** | コードレビュー・品質チェック | anthropic/claude-sonnet-4-5 | subagent |
|
|
120
169
|
| **atlas** | タスク管理・進捗追跡 | anthropic/claude-sonnet-4-5 | subagent |
|
|
121
170
|
| **multimodal-looker** | 画像・スクリーンショット解析 | anthropic/claude-sonnet-4-5 | subagent |
|
|
171
|
+
| **scout** | プラグインエコシステム調査・信頼度メタデータ収集 | anthropic/claude-sonnet-4-5 | subagent |
|
|
122
172
|
|
|
123
173
|
全てのモデルは `oh-my-opencode.json` でオーバーライド可能。
|
|
124
174
|
|
|
@@ -190,6 +240,7 @@ ledger_load({})
|
|
|
190
240
|
| `search` / `find` / `探して` 等 | search | 網羅的検索 |
|
|
191
241
|
| `analyze` / `調査` / `debug` 等 | analyze | コンテキスト収集 |
|
|
192
242
|
| `think hard` / `じっくり` / `熟考` 等 | think | Extended thinking 有効化 |
|
|
243
|
+
| `evolve` / `self-improve` / `自己改善` 等 | evolve | 自己改善サイクル起動 |
|
|
193
244
|
|
|
194
245
|
日本語・中国語キーワードにも対応。
|
|
195
246
|
|
|
@@ -231,8 +282,8 @@ ledger_load({})
|
|
|
231
282
|
"disabled_hooks": [], // keyword-detector, rules-injector, context-injector,
|
|
232
283
|
// fragment-injector, prompt-renderer, comment-checker,
|
|
233
284
|
// token-truncation, todo-enforcer, session-compaction
|
|
234
|
-
"disabled_tools": [], // spawn_agent, ralph_loop, cancel_ralph,
|
|
235
|
-
//
|
|
285
|
+
"disabled_tools": [], // spawn_agent, ralph_loop, cancel_ralph, batch_read,
|
|
286
|
+
// ledger_save, ledger_load, ast_search, evolve_apply
|
|
236
287
|
"disabled_mcps": [], // context7
|
|
237
288
|
|
|
238
289
|
// Built-in Agent Demotion (default: true)
|
|
@@ -245,6 +296,12 @@ ledger_load({})
|
|
|
245
296
|
"modelConcurrency": { "openai/gpt-5.2": 2 }
|
|
246
297
|
},
|
|
247
298
|
|
|
299
|
+
// Safety
|
|
300
|
+
"safety": {
|
|
301
|
+
"maxTotalSpawned": 15, // 同時実行セッション上限
|
|
302
|
+
"agentTimeoutMs": 180000 // per-agent タイムアウト (ms)
|
|
303
|
+
},
|
|
304
|
+
|
|
248
305
|
// Token Truncation
|
|
249
306
|
"token_truncation": { "maxChars": 30000 },
|
|
250
307
|
|
|
@@ -270,7 +327,7 @@ opencode-ultra/
|
|
|
270
327
|
│ │ ├── types.ts # AgentDef 型定義
|
|
271
328
|
│ │ └── index.ts # ビルトインエージェント + 動的プロンプト生成
|
|
272
329
|
│ ├── hooks/
|
|
273
|
-
│ │ ├── keyword-detector.ts # ultrawork/search/analyze/think 検知
|
|
330
|
+
│ │ ├── keyword-detector.ts # ultrawork/search/analyze/think/evolve 検知
|
|
274
331
|
│ │ ├── rules-injector.ts # rules.md + ARCHITECTURE.md + CODE_STYLE.md 注入
|
|
275
332
|
│ │ ├── fragment-injector.ts # エージェント毎の断片注入
|
|
276
333
|
│ │ ├── prompt-renderer.ts # モデル別フォーマット変換
|
|
@@ -279,16 +336,18 @@ opencode-ultra/
|
|
|
279
336
|
│ │ ├── todo-enforcer.ts # 未完了 TODO 強制継続
|
|
280
337
|
│ │ └── session-compaction.ts # セッション圧縮サマリ生成
|
|
281
338
|
│ ├── tools/
|
|
282
|
-
│ │ ├── spawn-agent.ts # 並列エージェント実行
|
|
339
|
+
│ │ ├── spawn-agent.ts # 並列エージェント実行 + 安全制御
|
|
283
340
|
│ │ ├── ralph-loop.ts # 自律ループ実行
|
|
284
341
|
│ │ ├── batch-read.ts # 複数ファイル並列読み込み
|
|
285
342
|
│ │ ├── continuity-ledger.ts # セッション間文脈継続
|
|
286
|
-
│ │
|
|
343
|
+
│ │ ├── ast-search.ts # AST-aware コード検索
|
|
344
|
+
│ │ └── evolve-apply.ts # 信頼度スコア評価 + 設定適用
|
|
287
345
|
│ ├── concurrency/ # Semaphore + ConcurrencyPool
|
|
288
346
|
│ ├── categories/ # ビルトインカテゴリ
|
|
347
|
+
│ ├── safety/ # sanitizer + trust-score
|
|
289
348
|
│ ├── mcp/ # MCP サーバー登録
|
|
290
349
|
│ └── shared/ # ユーティリティ
|
|
291
|
-
├── __test__/ # Bun テスト
|
|
350
|
+
├── __test__/ # Bun テスト
|
|
292
351
|
├── package.json
|
|
293
352
|
├── tsconfig.json
|
|
294
353
|
└── .gitignore
|
package/dist/index.js
CHANGED
|
@@ -13552,6 +13552,9 @@ import { join } from "path";
|
|
|
13552
13552
|
function getConfigDir() {
|
|
13553
13553
|
return process.env.XDG_CONFIG_HOME ? join(process.env.XDG_CONFIG_HOME, "opencode") : join(homedir(), ".config", "opencode");
|
|
13554
13554
|
}
|
|
13555
|
+
function getCacheDir() {
|
|
13556
|
+
return process.env.XDG_CACHE_HOME ? join(process.env.XDG_CACHE_HOME, "opencode") : join(homedir(), ".cache", "opencode");
|
|
13557
|
+
}
|
|
13555
13558
|
// node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
13556
13559
|
function createScanner(text, ignoreTrivia = false) {
|
|
13557
13560
|
const len = text.length;
|
|
@@ -14640,35 +14643,50 @@ var BUILTIN_AGENTS = {
|
|
|
14640
14643
|
},
|
|
14641
14644
|
scout: {
|
|
14642
14645
|
model: "anthropic/claude-sonnet-4-5",
|
|
14643
|
-
description: "Plugin ecosystem researcher \u2014
|
|
14646
|
+
description: "Plugin ecosystem researcher \u2014 discovers features and techniques from other OpenCode plugins",
|
|
14644
14647
|
prompt: `You are Scout, an OpenCode plugin ecosystem researcher.
|
|
14645
14648
|
|
|
14646
14649
|
## YOUR MISSION
|
|
14647
|
-
Search
|
|
14648
|
-
|
|
14650
|
+
Search for OpenCode plugins and analyze WHAT THEY DO and HOW THEY DO IT.
|
|
14651
|
+
The goal is to find features and techniques that opencode-ultra can learn from \u2014 NOT to recommend installing them.
|
|
14649
14652
|
|
|
14650
14653
|
## SEARCH STRATEGY
|
|
14651
14654
|
1. Search npm for "opencode-plugin", "opencode-ai", "@opencode" packages
|
|
14652
14655
|
2. Search GitHub for "opencode plugin", "opencode extension", "oh-my-opencode"
|
|
14653
14656
|
3. Look at package.json dependencies on @opencode-ai/plugin or @opencode-ai/sdk
|
|
14654
|
-
4. Read README files and
|
|
14657
|
+
4. Read README files and SOURCE CODE of discovered plugins \u2014 understand their implementation
|
|
14658
|
+
|
|
14659
|
+
## WHAT TO ANALYZE PER PLUGIN
|
|
14660
|
+
- **name**: package name + repo URL
|
|
14661
|
+
- **description**: what it does
|
|
14662
|
+
- **features**: detailed list of capabilities
|
|
14663
|
+
- **hooks used**: which OpenCode hooks (chat.message, tool.execute.after, etc.)
|
|
14664
|
+
- **tools provided**: custom tools and what they do
|
|
14665
|
+
- **techniques**: interesting implementation patterns (e.g. caching strategies, prompt engineering, API usage)
|
|
14666
|
+
- **unique ideas**: features/approaches NOT in opencode-ultra
|
|
14667
|
+
|
|
14668
|
+
## WHAT TO SKIP
|
|
14669
|
+
- Auth plugins (opencode-*-auth) \u2014 domain-specific, not relevant
|
|
14670
|
+
- Abandoned/empty repos (no commits in 6+ months, no README)
|
|
14671
|
+
- Trivial wrappers (just re-exports or single-function plugins)
|
|
14655
14672
|
|
|
14656
14673
|
## OUTPUT FORMAT
|
|
14657
|
-
For each plugin
|
|
14658
|
-
- **Name**: package name + repo URL
|
|
14659
|
-
- **Version**: latest version
|
|
14660
|
-
- **Features**: bullet list of capabilities
|
|
14661
|
-
- **Architecture**: hook types used, tool count, agent count
|
|
14662
|
-
- **Quality signals**: test count, TypeScript, last updated, download count
|
|
14663
|
-
- **Unique ideas**: features that opencode-ultra does NOT have
|
|
14674
|
+
For each INTERESTING plugin (skip the trivial ones):
|
|
14664
14675
|
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
-
|
|
14668
|
-
- Features
|
|
14669
|
-
-
|
|
14676
|
+
### [plugin-name]
|
|
14677
|
+
- **What**: one-line description
|
|
14678
|
+
- **Repo**: URL
|
|
14679
|
+
- **Features**: bullet list
|
|
14680
|
+
- **Hooks**: which hooks and how they use them
|
|
14681
|
+
- **Tools**: custom tools and purpose
|
|
14682
|
+
- **Interesting technique**: what they do that's clever or useful
|
|
14683
|
+
- **Applicable to opencode-ultra**: YES/NO + what we could adapt
|
|
14670
14684
|
|
|
14671
|
-
|
|
14685
|
+
End with a summary section:
|
|
14686
|
+
## Features opencode-ultra could adopt
|
|
14687
|
+
(Ranked by impact \u2014 high/medium/low)
|
|
14688
|
+
|
|
14689
|
+
Be thorough but focused on QUALITY over quantity. 5 well-analyzed plugins beats 20 superficial listings.`,
|
|
14672
14690
|
mode: "subagent",
|
|
14673
14691
|
maxTokens: 32000
|
|
14674
14692
|
}
|
|
@@ -14906,30 +14924,70 @@ var THINK_MESSAGE = `Extended thinking enabled. Take your time to reason thoroug
|
|
|
14906
14924
|
var EVOLVE_MESSAGE = `[evolve-mode] SELF-IMPROVEMENT CYCLE ACTIVATED.
|
|
14907
14925
|
|
|
14908
14926
|
## MISSION
|
|
14909
|
-
|
|
14927
|
+
Discover what other OpenCode plugins do well, compare with opencode-ultra's current capabilities, and propose concrete improvements to opencode-ultra itself.
|
|
14928
|
+
|
|
14929
|
+
This is NOT about installing other plugins. This is about LEARNING from the ecosystem and making opencode-ultra better.
|
|
14910
14930
|
|
|
14911
|
-
##
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
3. **COMPARE** \u2014 Generate a structured gap analysis (what we have vs what we're missing)
|
|
14915
|
-
4. **PRIORITIZE** \u2014 Rank missing features by impact (high/medium/low)
|
|
14916
|
-
5. **PROPOSE** \u2014 Present improvement plan to the user
|
|
14917
|
-
6. **SAVE** \u2014 Save findings to a continuity ledger via ledger_save for future reference
|
|
14931
|
+
## TOOLS AVAILABLE
|
|
14932
|
+
- **spawn_agent** \u2014 run scout + explore agents in parallel for data gathering
|
|
14933
|
+
- **ledger_save** \u2014 persist improvement proposals for future implementation
|
|
14918
14934
|
|
|
14919
|
-
##
|
|
14935
|
+
## PHASE 1: GATHER (parallel)
|
|
14920
14936
|
\`\`\`
|
|
14921
14937
|
spawn_agent({
|
|
14922
14938
|
agents: [
|
|
14923
|
-
{agent: "scout", prompt: "Search npm and GitHub for OpenCode 1.2.x plugins.
|
|
14924
|
-
{agent: "explore", prompt: "Read opencode-ultra's
|
|
14939
|
+
{agent: "scout", prompt: "Search npm and GitHub for OpenCode 1.2.x plugins. For EACH plugin, analyze: what features does it provide? What hooks, tools, or techniques does it use? Focus on UNIQUE capabilities that are genuinely useful. Return a structured feature inventory per plugin.", description: "Ecosystem feature scan"},
|
|
14940
|
+
{agent: "explore", prompt: "Read opencode-ultra's source: src/index.ts, src/tools/*.ts, src/hooks/*.ts, src/safety/*.ts, src/agents/index.ts, README.md. Catalog every feature, tool, hook, and capability. Be exhaustive.", description: "Self-analysis"}
|
|
14925
14941
|
]
|
|
14926
14942
|
})
|
|
14927
14943
|
\`\`\`
|
|
14928
14944
|
|
|
14929
|
-
|
|
14930
|
-
|
|
14945
|
+
## PHASE 2: COMPARE
|
|
14946
|
+
After gathering results, build a structured gap analysis:
|
|
14947
|
+
|
|
14948
|
+
### Feature Matrix
|
|
14949
|
+
| Feature | opencode-ultra | Other plugin(s) | Gap? |
|
|
14950
|
+
|---------|---------------|-----------------|------|
|
|
14951
|
+
| (feature) | Yes/No | Which plugin has it | Missing / Partial / Covered |
|
|
14952
|
+
|
|
14953
|
+
Focus on features that are:
|
|
14954
|
+
- **Genuinely useful** (not gimmicks)
|
|
14955
|
+
- **Feasible to implement** (not requiring external infrastructure)
|
|
14956
|
+
- **Complementary** to existing capabilities (not duplicate)
|
|
14957
|
+
|
|
14958
|
+
### What to IGNORE
|
|
14959
|
+
- Auth plugins (opencode-antigravity-auth etc.) \u2014 domain-specific, not relevant
|
|
14960
|
+
- oh-my-opencode features we already ported \u2014 mark as "Covered"
|
|
14961
|
+
- Trivial wrappers or abandoned projects
|
|
14962
|
+
|
|
14963
|
+
## PHASE 3: PROPOSE
|
|
14964
|
+
For each identified gap, produce a concrete improvement proposal:
|
|
14965
|
+
|
|
14966
|
+
\`\`\`
|
|
14967
|
+
## Improvement: [Feature Name]
|
|
14968
|
+
**Inspiration**: [Plugin name] \u2014 [what it does]
|
|
14969
|
+
**Why**: [Why opencode-ultra needs this]
|
|
14970
|
+
**How**: [Implementation sketch \u2014 which file to modify, what to add]
|
|
14971
|
+
**Effort**: Low / Medium / High
|
|
14972
|
+
**Priority**: P0 (critical) / P1 (important) / P2 (nice-to-have)
|
|
14973
|
+
\`\`\`
|
|
14974
|
+
|
|
14975
|
+
Sort proposals by Priority then Effort (P0-Low first, P2-High last).
|
|
14976
|
+
|
|
14977
|
+
## PHASE 4: SAVE
|
|
14978
|
+
Save the full analysis to the continuity ledger:
|
|
14979
|
+
\`\`\`
|
|
14980
|
+
ledger_save({
|
|
14981
|
+
name: "evolve-scan-YYYY-MM-DD",
|
|
14982
|
+
content: "# Evolve Scan Results\\n\\n## Feature Matrix\\n...\\n## Improvement Proposals\\n..."
|
|
14983
|
+
})
|
|
14984
|
+
\`\`\`
|
|
14931
14985
|
|
|
14932
|
-
|
|
14986
|
+
## IMPORTANT
|
|
14987
|
+
- The goal is to make opencode-ultra BETTER, not to install other plugins.
|
|
14988
|
+
- Other plugins are REFERENCE MATERIAL \u2014 study their approach, then design our own implementation.
|
|
14989
|
+
- Every proposal must include a concrete "How" section with file paths and implementation direction.
|
|
14990
|
+
- Present the final proposals to the user for approval before any implementation.`;
|
|
14933
14991
|
|
|
14934
14992
|
// src/hooks/rules-injector.ts
|
|
14935
14993
|
import * as fs2 from "fs";
|
|
@@ -27489,6 +27547,182 @@ function sanitizeSpawnResult(result) {
|
|
|
27489
27547
|
return banner + `
|
|
27490
27548
|
` + text;
|
|
27491
27549
|
}
|
|
27550
|
+
// src/safety/trust-score.ts
|
|
27551
|
+
var KNOWN_SCOPES = [
|
|
27552
|
+
"@opencode-ai/",
|
|
27553
|
+
"opencode-",
|
|
27554
|
+
"@tarquinen/",
|
|
27555
|
+
"@azumag/",
|
|
27556
|
+
"@hoangp8/",
|
|
27557
|
+
"@gitlab/"
|
|
27558
|
+
];
|
|
27559
|
+
var TYPOSQUAT_PATTERNS = [
|
|
27560
|
+
/^0pencode/i,
|
|
27561
|
+
/^opencodd/i,
|
|
27562
|
+
/^openc0de/i,
|
|
27563
|
+
/^opencodee/i,
|
|
27564
|
+
/^opeencode/i,
|
|
27565
|
+
/^opemcode/i,
|
|
27566
|
+
/^opencode-.{1,3}$/i
|
|
27567
|
+
];
|
|
27568
|
+
function computeTrustScore(meta3) {
|
|
27569
|
+
const factors = [];
|
|
27570
|
+
factors.push(scoreRecency(meta3));
|
|
27571
|
+
factors.push(scorePopularity(meta3));
|
|
27572
|
+
factors.push(scoreQuality(meta3));
|
|
27573
|
+
factors.push(scoreRepository(meta3));
|
|
27574
|
+
factors.push(scoreSafety(meta3));
|
|
27575
|
+
const totalScore = Math.min(100, factors.reduce((sum, f) => sum + f.score, 0));
|
|
27576
|
+
const level = classifyLevel(totalScore);
|
|
27577
|
+
const summary = buildSummary(meta3.name, totalScore, level, factors);
|
|
27578
|
+
log("Trust score computed", { package: meta3.name, score: totalScore, level });
|
|
27579
|
+
return { score: totalScore, level, factors, summary };
|
|
27580
|
+
}
|
|
27581
|
+
function scoreRecency(meta3) {
|
|
27582
|
+
const maxScore = 25;
|
|
27583
|
+
if (!meta3.lastPublished) {
|
|
27584
|
+
return { name: "recency", score: 0, maxScore, detail: "No publish date available" };
|
|
27585
|
+
}
|
|
27586
|
+
const daysAgo = daysSince(meta3.lastPublished);
|
|
27587
|
+
if (daysAgo < 30)
|
|
27588
|
+
return { name: "recency", score: 25, maxScore, detail: `Updated ${daysAgo}d ago` };
|
|
27589
|
+
if (daysAgo < 90)
|
|
27590
|
+
return { name: "recency", score: 20, maxScore, detail: `Updated ${daysAgo}d ago` };
|
|
27591
|
+
if (daysAgo < 180)
|
|
27592
|
+
return { name: "recency", score: 15, maxScore, detail: `Updated ${daysAgo}d ago` };
|
|
27593
|
+
if (daysAgo < 365)
|
|
27594
|
+
return { name: "recency", score: 10, maxScore, detail: `Updated ${daysAgo}d ago \u2014 getting stale` };
|
|
27595
|
+
return { name: "recency", score: 3, maxScore, detail: `Updated ${daysAgo}d ago \u2014 likely abandoned` };
|
|
27596
|
+
}
|
|
27597
|
+
function scorePopularity(meta3) {
|
|
27598
|
+
const maxScore = 25;
|
|
27599
|
+
let score = 0;
|
|
27600
|
+
const details = [];
|
|
27601
|
+
const dl = meta3.weeklyDownloads ?? 0;
|
|
27602
|
+
if (dl > 1e4) {
|
|
27603
|
+
score += 15;
|
|
27604
|
+
details.push(`${dl.toLocaleString()} weekly DL`);
|
|
27605
|
+
} else if (dl > 1000) {
|
|
27606
|
+
score += 12;
|
|
27607
|
+
details.push(`${dl.toLocaleString()} weekly DL`);
|
|
27608
|
+
} else if (dl > 100) {
|
|
27609
|
+
score += 8;
|
|
27610
|
+
details.push(`${dl} weekly DL`);
|
|
27611
|
+
} else if (dl > 10) {
|
|
27612
|
+
score += 4;
|
|
27613
|
+
details.push(`${dl} weekly DL \u2014 low`);
|
|
27614
|
+
} else {
|
|
27615
|
+
score += 0;
|
|
27616
|
+
details.push(`${dl} weekly DL \u2014 very low`);
|
|
27617
|
+
}
|
|
27618
|
+
const stars = meta3.stars ?? 0;
|
|
27619
|
+
if (stars > 100) {
|
|
27620
|
+
score += 10;
|
|
27621
|
+
details.push(`${stars} stars`);
|
|
27622
|
+
} else if (stars > 20) {
|
|
27623
|
+
score += 7;
|
|
27624
|
+
details.push(`${stars} stars`);
|
|
27625
|
+
} else if (stars > 5) {
|
|
27626
|
+
score += 4;
|
|
27627
|
+
details.push(`${stars} stars`);
|
|
27628
|
+
} else {
|
|
27629
|
+
score += 0;
|
|
27630
|
+
details.push(`${stars} stars`);
|
|
27631
|
+
}
|
|
27632
|
+
return { name: "popularity", score: Math.min(maxScore, score), maxScore, detail: details.join(", ") };
|
|
27633
|
+
}
|
|
27634
|
+
function scoreQuality(meta3) {
|
|
27635
|
+
const maxScore = 20;
|
|
27636
|
+
let score = 0;
|
|
27637
|
+
const details = [];
|
|
27638
|
+
if (meta3.license && meta3.license !== "NONE" && meta3.license !== "UNLICENSED") {
|
|
27639
|
+
score += 7;
|
|
27640
|
+
details.push(`License: ${meta3.license}`);
|
|
27641
|
+
} else {
|
|
27642
|
+
details.push("No license");
|
|
27643
|
+
}
|
|
27644
|
+
if (meta3.hasReadme !== false) {
|
|
27645
|
+
score += 5;
|
|
27646
|
+
details.push("Has README");
|
|
27647
|
+
} else {
|
|
27648
|
+
details.push("No README");
|
|
27649
|
+
}
|
|
27650
|
+
const maintainers = meta3.maintainerCount ?? 0;
|
|
27651
|
+
if (maintainers >= 2) {
|
|
27652
|
+
score += 5;
|
|
27653
|
+
details.push(`${maintainers} maintainers`);
|
|
27654
|
+
} else if (maintainers === 1) {
|
|
27655
|
+
score += 3;
|
|
27656
|
+
details.push("1 maintainer");
|
|
27657
|
+
} else {
|
|
27658
|
+
details.push("No maintainer info");
|
|
27659
|
+
}
|
|
27660
|
+
if (meta3.description && meta3.description.length > 20) {
|
|
27661
|
+
score += 3;
|
|
27662
|
+
} else {
|
|
27663
|
+
details.push("Weak description");
|
|
27664
|
+
}
|
|
27665
|
+
return { name: "quality", score: Math.min(maxScore, score), maxScore, detail: details.join(", ") };
|
|
27666
|
+
}
|
|
27667
|
+
function scoreRepository(meta3) {
|
|
27668
|
+
const maxScore = 15;
|
|
27669
|
+
if (!meta3.repository) {
|
|
27670
|
+
return { name: "repository", score: 0, maxScore, detail: "No repository link \u2014 suspicious" };
|
|
27671
|
+
}
|
|
27672
|
+
if (meta3.repository.includes("github.com")) {
|
|
27673
|
+
return { name: "repository", score: 15, maxScore, detail: `Repo: ${meta3.repository}` };
|
|
27674
|
+
}
|
|
27675
|
+
if (meta3.repository.includes("gitlab.com") || meta3.repository.includes("bitbucket.org")) {
|
|
27676
|
+
return { name: "repository", score: 12, maxScore, detail: `Repo: ${meta3.repository}` };
|
|
27677
|
+
}
|
|
27678
|
+
return { name: "repository", score: 5, maxScore, detail: `Repo: ${meta3.repository} (unknown host)` };
|
|
27679
|
+
}
|
|
27680
|
+
function scoreSafety(meta3) {
|
|
27681
|
+
const maxScore = 15;
|
|
27682
|
+
let score = 15;
|
|
27683
|
+
const details = [];
|
|
27684
|
+
if (isTyposquatSuspect(meta3.name)) {
|
|
27685
|
+
score -= 15;
|
|
27686
|
+
details.push("TYPOSQUAT SUSPECT");
|
|
27687
|
+
} else {
|
|
27688
|
+
details.push("Name OK");
|
|
27689
|
+
}
|
|
27690
|
+
if (KNOWN_SCOPES.some((s) => meta3.name.startsWith(s))) {
|
|
27691
|
+
score = Math.min(maxScore, score + 3);
|
|
27692
|
+
details.push("Known scope");
|
|
27693
|
+
}
|
|
27694
|
+
const deps = meta3.dependencyCount ?? 0;
|
|
27695
|
+
if (deps > 50) {
|
|
27696
|
+
score = Math.max(0, score - 5);
|
|
27697
|
+
details.push(`${deps} deps \u2014 heavy`);
|
|
27698
|
+
} else if (deps > 20) {
|
|
27699
|
+
score = Math.max(0, score - 2);
|
|
27700
|
+
details.push(`${deps} deps`);
|
|
27701
|
+
}
|
|
27702
|
+
return { name: "safety", score: Math.max(0, score), maxScore, detail: details.join(", ") };
|
|
27703
|
+
}
|
|
27704
|
+
function isTyposquatSuspect(name) {
|
|
27705
|
+
return TYPOSQUAT_PATTERNS.some((p) => p.test(name));
|
|
27706
|
+
}
|
|
27707
|
+
function classifyLevel(score) {
|
|
27708
|
+
if (score >= 90)
|
|
27709
|
+
return "high";
|
|
27710
|
+
if (score >= 70)
|
|
27711
|
+
return "medium";
|
|
27712
|
+
if (score >= 40)
|
|
27713
|
+
return "low";
|
|
27714
|
+
return "risky";
|
|
27715
|
+
}
|
|
27716
|
+
function daysSince(isoDate) {
|
|
27717
|
+
const then = new Date(isoDate).getTime();
|
|
27718
|
+
const now = Date.now();
|
|
27719
|
+
return Math.floor((now - then) / (1000 * 60 * 60 * 24));
|
|
27720
|
+
}
|
|
27721
|
+
function buildSummary(name, score, level, factors) {
|
|
27722
|
+
const icon = level === "high" ? "V" : level === "medium" ? "~" : level === "low" ? "!" : "X";
|
|
27723
|
+
const weakest = [...factors].sort((a, b) => a.score / a.maxScore - b.score / b.maxScore)[0];
|
|
27724
|
+
return `[${icon}] ${name}: ${score}/100 (${level}) \u2014 weakest: ${weakest.name} (${weakest.score}/${weakest.maxScore}: ${weakest.detail})`;
|
|
27725
|
+
}
|
|
27492
27726
|
// src/tools/spawn-agent.ts
|
|
27493
27727
|
var DEFAULT_MAX_TOTAL_SPAWNED = 15;
|
|
27494
27728
|
var DEFAULT_AGENT_TIMEOUT_MS = 180000;
|
|
@@ -28086,6 +28320,199 @@ function createAstSearchTool(ctx, binaryPath) {
|
|
|
28086
28320
|
});
|
|
28087
28321
|
}
|
|
28088
28322
|
|
|
28323
|
+
// src/tools/evolve-apply.ts
|
|
28324
|
+
import * as fs7 from "fs";
|
|
28325
|
+
import * as path7 from "path";
|
|
28326
|
+
function readJson(filePath) {
|
|
28327
|
+
try {
|
|
28328
|
+
return JSON.parse(fs7.readFileSync(filePath, "utf-8"));
|
|
28329
|
+
} catch {
|
|
28330
|
+
return null;
|
|
28331
|
+
}
|
|
28332
|
+
}
|
|
28333
|
+
function writeJson(filePath, data) {
|
|
28334
|
+
fs7.writeFileSync(filePath, JSON.stringify(data, null, 2) + `
|
|
28335
|
+
`, "utf-8");
|
|
28336
|
+
}
|
|
28337
|
+
function createBackup(configDir) {
|
|
28338
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
28339
|
+
const backupDir = path7.join(configDir, "backups", `pre-evolve-${ts}`);
|
|
28340
|
+
fs7.mkdirSync(backupDir, { recursive: true });
|
|
28341
|
+
const configPath = path7.join(configDir, "opencode.json");
|
|
28342
|
+
const pkgPath = path7.join(configDir, "package.json");
|
|
28343
|
+
const cachePkgPath = path7.join(getCacheDir(), "package.json");
|
|
28344
|
+
if (fs7.existsSync(configPath)) {
|
|
28345
|
+
fs7.copyFileSync(configPath, path7.join(backupDir, "opencode.json"));
|
|
28346
|
+
}
|
|
28347
|
+
if (fs7.existsSync(pkgPath)) {
|
|
28348
|
+
fs7.copyFileSync(pkgPath, path7.join(backupDir, "package.json"));
|
|
28349
|
+
}
|
|
28350
|
+
if (fs7.existsSync(cachePkgPath)) {
|
|
28351
|
+
fs7.copyFileSync(cachePkgPath, path7.join(backupDir, "cache-package.json"));
|
|
28352
|
+
}
|
|
28353
|
+
log("Evolve backup created", { path: backupDir });
|
|
28354
|
+
return backupDir;
|
|
28355
|
+
}
|
|
28356
|
+
function createEvolveApplyTool(ctx) {
|
|
28357
|
+
return tool({
|
|
28358
|
+
description: `Apply evolve scan recommendations to OpenCode configuration.
|
|
28359
|
+
Takes a list of plugin recommendations (from evolve scan), computes trust scores,
|
|
28360
|
+
creates a backup, and adds approved plugins to opencode.json and package.json.
|
|
28361
|
+
|
|
28362
|
+
IMPORTANT: Only plugins with trust score >= 40 (low or above) are added.
|
|
28363
|
+
Risky plugins (score < 40) are skipped with a warning.
|
|
28364
|
+
|
|
28365
|
+
Use dryRun: true to preview changes without applying them.
|
|
28366
|
+
|
|
28367
|
+
Example:
|
|
28368
|
+
evolve_apply({
|
|
28369
|
+
plugins: [
|
|
28370
|
+
{name: "opencode-supermemory", version: "latest", reason: "Persistent memory across sessions"},
|
|
28371
|
+
{name: "@azumag/opencode-rate-limit-fallback", version: "^1.0.0", reason: "Rate limit handling"}
|
|
28372
|
+
],
|
|
28373
|
+
dryRun: false
|
|
28374
|
+
})`,
|
|
28375
|
+
args: {
|
|
28376
|
+
plugins: tool.schema.array(tool.schema.object({
|
|
28377
|
+
name: tool.schema.string().describe("npm package name"),
|
|
28378
|
+
version: tool.schema.string().optional().describe("Version spec (default: latest)"),
|
|
28379
|
+
reason: tool.schema.string().describe("Why this plugin is recommended"),
|
|
28380
|
+
metadata: tool.schema.object({
|
|
28381
|
+
name: tool.schema.string(),
|
|
28382
|
+
lastPublished: tool.schema.string().optional(),
|
|
28383
|
+
weeklyDownloads: tool.schema.number().optional(),
|
|
28384
|
+
stars: tool.schema.number().optional(),
|
|
28385
|
+
repository: tool.schema.string().optional(),
|
|
28386
|
+
license: tool.schema.string().optional(),
|
|
28387
|
+
hasReadme: tool.schema.boolean().optional(),
|
|
28388
|
+
maintainerCount: tool.schema.number().optional(),
|
|
28389
|
+
dependencyCount: tool.schema.number().optional()
|
|
28390
|
+
}).optional().describe("Package metadata for trust scoring (if available)")
|
|
28391
|
+
})).describe("Plugins to evaluate and potentially install"),
|
|
28392
|
+
dryRun: tool.schema.boolean().optional().describe("Preview changes without applying (default: false)"),
|
|
28393
|
+
minTrustScore: tool.schema.number().optional().describe("Minimum trust score to auto-install (default: 40)")
|
|
28394
|
+
},
|
|
28395
|
+
execute: async (args) => {
|
|
28396
|
+
const { plugins } = args;
|
|
28397
|
+
const dryRun = args.dryRun ?? false;
|
|
28398
|
+
const minScore = args.minTrustScore ?? 40;
|
|
28399
|
+
if (!plugins || plugins.length === 0) {
|
|
28400
|
+
return "No plugins specified.";
|
|
28401
|
+
}
|
|
28402
|
+
const configDir = getConfigDir();
|
|
28403
|
+
const configPath = path7.join(configDir, "opencode.json");
|
|
28404
|
+
const cachePkgPath = path7.join(getCacheDir(), "package.json");
|
|
28405
|
+
const config3 = readJson(configPath);
|
|
28406
|
+
if (!config3) {
|
|
28407
|
+
return `Error: Cannot read ${configPath}`;
|
|
28408
|
+
}
|
|
28409
|
+
const currentPlugins = config3.plugin ?? [];
|
|
28410
|
+
const cachePkg = readJson(cachePkgPath);
|
|
28411
|
+
const currentDeps = cachePkg?.dependencies ?? {};
|
|
28412
|
+
const added = [];
|
|
28413
|
+
const skipped = [];
|
|
28414
|
+
const trustLines = [];
|
|
28415
|
+
for (const rec of plugins) {
|
|
28416
|
+
const alreadyInConfig = currentPlugins.some((p) => p === rec.name || p.startsWith(rec.name + "@"));
|
|
28417
|
+
const alreadyInDeps = rec.name in currentDeps;
|
|
28418
|
+
if (alreadyInConfig || alreadyInDeps) {
|
|
28419
|
+
skipped.push({ name: rec.name, reason: "Already installed" });
|
|
28420
|
+
trustLines.push(` SKIP ${rec.name} \u2014 already installed`);
|
|
28421
|
+
continue;
|
|
28422
|
+
}
|
|
28423
|
+
const meta3 = rec.metadata ?? { name: rec.name };
|
|
28424
|
+
const trustResult = computeTrustScore(meta3);
|
|
28425
|
+
const scoreTag = `[${trustResult.score}/100 ${trustResult.level}]`;
|
|
28426
|
+
trustLines.push(` ${scoreTag} ${rec.name} \u2014 ${rec.reason}`);
|
|
28427
|
+
for (const f of trustResult.factors) {
|
|
28428
|
+
trustLines.push(` ${f.name}: ${f.score}/${f.maxScore} (${f.detail})`);
|
|
28429
|
+
}
|
|
28430
|
+
if (trustResult.score < minScore) {
|
|
28431
|
+
skipped.push({
|
|
28432
|
+
name: rec.name,
|
|
28433
|
+
reason: `Trust score ${trustResult.score} < ${minScore} (${trustResult.level})`
|
|
28434
|
+
});
|
|
28435
|
+
trustLines.push(` -> REJECTED (below threshold ${minScore})`);
|
|
28436
|
+
continue;
|
|
28437
|
+
}
|
|
28438
|
+
added.push(rec.name);
|
|
28439
|
+
trustLines.push(` -> APPROVED`);
|
|
28440
|
+
}
|
|
28441
|
+
const trustReport = trustLines.join(`
|
|
28442
|
+
`);
|
|
28443
|
+
if (added.length === 0) {
|
|
28444
|
+
log("Evolve apply: nothing to add", { skipped: skipped.length });
|
|
28445
|
+
return formatResult({
|
|
28446
|
+
added: [],
|
|
28447
|
+
skipped,
|
|
28448
|
+
dryRun,
|
|
28449
|
+
trustReport
|
|
28450
|
+
});
|
|
28451
|
+
}
|
|
28452
|
+
if (dryRun) {
|
|
28453
|
+
log("Evolve apply: dry run", { wouldAdd: added });
|
|
28454
|
+
return formatResult({ added, skipped, dryRun: true, trustReport });
|
|
28455
|
+
}
|
|
28456
|
+
const backupDir = createBackup(configDir);
|
|
28457
|
+
const pluginEntry = (name, version3) => version3 && version3 !== "latest" ? `${name}@${version3}` : name;
|
|
28458
|
+
for (const name of added) {
|
|
28459
|
+
const rec = plugins.find((p) => p.name === name);
|
|
28460
|
+
const entry = pluginEntry(name, rec.version);
|
|
28461
|
+
if (!currentPlugins.includes(entry)) {
|
|
28462
|
+
currentPlugins.push(entry);
|
|
28463
|
+
}
|
|
28464
|
+
}
|
|
28465
|
+
config3.plugin = currentPlugins;
|
|
28466
|
+
writeJson(configPath, config3);
|
|
28467
|
+
log("Evolve apply: updated opencode.json", { plugins: currentPlugins });
|
|
28468
|
+
if (cachePkg) {
|
|
28469
|
+
const deps = cachePkg.dependencies ?? {};
|
|
28470
|
+
for (const name of added) {
|
|
28471
|
+
const rec = plugins.find((p) => p.name === name);
|
|
28472
|
+
deps[name] = rec.version ?? "latest";
|
|
28473
|
+
}
|
|
28474
|
+
cachePkg.dependencies = deps;
|
|
28475
|
+
writeJson(cachePkgPath, cachePkg);
|
|
28476
|
+
log("Evolve apply: updated cache/package.json");
|
|
28477
|
+
}
|
|
28478
|
+
return formatResult({ added, skipped, backup: backupDir, dryRun: false, trustReport });
|
|
28479
|
+
}
|
|
28480
|
+
});
|
|
28481
|
+
}
|
|
28482
|
+
function formatResult(result) {
|
|
28483
|
+
const lines = [];
|
|
28484
|
+
lines.push(result.dryRun ? "## Evolve Apply (DRY RUN)" : "## Evolve Apply Results");
|
|
28485
|
+
lines.push("");
|
|
28486
|
+
if (result.backup) {
|
|
28487
|
+
lines.push(`**Backup**: \`${result.backup}\``);
|
|
28488
|
+
lines.push("");
|
|
28489
|
+
}
|
|
28490
|
+
lines.push("### Trust Score Report");
|
|
28491
|
+
lines.push("```");
|
|
28492
|
+
lines.push(result.trustReport);
|
|
28493
|
+
lines.push("```");
|
|
28494
|
+
lines.push("");
|
|
28495
|
+
if (result.added.length > 0) {
|
|
28496
|
+
lines.push(result.dryRun ? "### Would Add" : "### Added");
|
|
28497
|
+
for (const name of result.added) {
|
|
28498
|
+
lines.push(`- ${name}`);
|
|
28499
|
+
}
|
|
28500
|
+
lines.push("");
|
|
28501
|
+
}
|
|
28502
|
+
if (result.skipped.length > 0) {
|
|
28503
|
+
lines.push("### Skipped");
|
|
28504
|
+
for (const { name, reason } of result.skipped) {
|
|
28505
|
+
lines.push(`- ${name} \u2014 ${reason}`);
|
|
28506
|
+
}
|
|
28507
|
+
lines.push("");
|
|
28508
|
+
}
|
|
28509
|
+
if (!result.dryRun && result.added.length > 0) {
|
|
28510
|
+
lines.push("> **Note**: Run `bun install` in `~/.cache/opencode/` to install the new packages, then restart OpenCode.");
|
|
28511
|
+
}
|
|
28512
|
+
return lines.join(`
|
|
28513
|
+
`);
|
|
28514
|
+
}
|
|
28515
|
+
|
|
28089
28516
|
// src/hooks/todo-enforcer.ts
|
|
28090
28517
|
var DEFAULT_MAX_ENFORCEMENTS = 5;
|
|
28091
28518
|
var sessionState = new Map;
|
|
@@ -28168,7 +28595,7 @@ ${unfinished.map((t) => `- [ ] ${t.text}`).join(`
|
|
|
28168
28595
|
}
|
|
28169
28596
|
|
|
28170
28597
|
// src/hooks/comment-checker.ts
|
|
28171
|
-
import * as
|
|
28598
|
+
import * as fs8 from "fs";
|
|
28172
28599
|
var AI_SLOP_PATTERNS = [
|
|
28173
28600
|
/\/\/ .{80,}/,
|
|
28174
28601
|
/\/\/ (This|The|We|Here|Note:)/i,
|
|
@@ -28247,7 +28674,7 @@ function createCommentCheckerHook(internalSessions, maxRatio = 0.3, slopThreshol
|
|
|
28247
28674
|
if (!filePath || !isCodeFile(filePath))
|
|
28248
28675
|
return;
|
|
28249
28676
|
try {
|
|
28250
|
-
const content =
|
|
28677
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
28251
28678
|
const result = checkComments(content, filePath, maxRatio, slopThreshold);
|
|
28252
28679
|
if (result.shouldWarn) {
|
|
28253
28680
|
output.output += `
|
|
@@ -28555,6 +28982,7 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
28555
28982
|
const ledgerLoad = createLedgerLoadTool(ctx);
|
|
28556
28983
|
const astGrepBin = findAstGrepBinary();
|
|
28557
28984
|
const astSearch = astGrepBin ? createAstSearchTool(ctx, astGrepBin) : null;
|
|
28985
|
+
const evolveApply = createEvolveApplyTool(ctx);
|
|
28558
28986
|
const todoEnforcer = createTodoEnforcer(ctx, internalSessions, pluginConfig.todo_enforcer?.maxEnforcements);
|
|
28559
28987
|
const commentCheckerHook = createCommentCheckerHook(internalSessions, pluginConfig.comment_checker?.maxRatio, pluginConfig.comment_checker?.slopThreshold);
|
|
28560
28988
|
const tokenTruncationHook = createTokenTruncationHook(internalSessions, pluginConfig.token_truncation?.maxChars);
|
|
@@ -28593,6 +29021,9 @@ var OpenCodeUltra = async (ctx) => {
|
|
|
28593
29021
|
if (!disabledTools.has("ast_search") && astSearch) {
|
|
28594
29022
|
toolRegistry.ast_search = astSearch;
|
|
28595
29023
|
}
|
|
29024
|
+
if (!disabledTools.has("evolve_apply")) {
|
|
29025
|
+
toolRegistry.evolve_apply = evolveApply;
|
|
29026
|
+
}
|
|
28596
29027
|
return {
|
|
28597
29028
|
tool: toolRegistry,
|
|
28598
29029
|
config: async (config3) => {
|
package/package.json
CHANGED