Compare commits
110 Commits
v3.10.0
...
fix/subage
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21ddd55162 | ||
|
|
5137df72d8 | ||
|
|
dd70ce37f0 | ||
|
|
7e0a1a133c | ||
|
|
be606cdfbe | ||
|
|
6a29a373f4 | ||
|
|
389625cb20 | ||
|
|
e916d564a9 | ||
|
|
3d8f390b9e | ||
|
|
a61f8bb853 | ||
|
|
c8c99445ea | ||
|
|
fc41a389c5 | ||
|
|
39d94a4af6 | ||
|
|
acf4c46439 | ||
|
|
5cbf7828f0 | ||
|
|
0efd1b65bb | ||
|
|
f8d2bd55b9 | ||
|
|
1ef8d73ce5 | ||
|
|
2b7524b1cb | ||
|
|
d6b0e564bf | ||
|
|
6897761b21 | ||
|
|
fe66b68baa | ||
|
|
a7f794c7a3 | ||
|
|
85690b69a8 | ||
|
|
8c2dcb75cb | ||
|
|
1ef5c17c35 | ||
|
|
42641a9922 | ||
|
|
63b783ba72 | ||
|
|
840af692a0 | ||
|
|
2175d58f5d | ||
|
|
23e1a42690 | ||
|
|
ceb8b239ac | ||
|
|
6e57479ec1 | ||
|
|
7fe2746e96 | ||
|
|
f983099957 | ||
|
|
f9da00d021 | ||
|
|
51a3d20dc9 | ||
|
|
785dd529e1 | ||
|
|
025d2a3579 | ||
|
|
0e858ee1df | ||
|
|
5ba9f37d8b | ||
|
|
b5100d99df | ||
|
|
4123148376 | ||
|
|
95fe698817 | ||
|
|
031967857f | ||
|
|
c80a74c5f4 | ||
|
|
3d66a30406 | ||
|
|
cf40ca5553 | ||
|
|
d4033da41a | ||
|
|
3363f0c63a | ||
|
|
c084cc3f26 | ||
|
|
f383d7abb5 | ||
|
|
33d39597ae | ||
|
|
3d4269dcf9 | ||
|
|
47e300b17e | ||
|
|
243ce1b7e8 | ||
|
|
ddeb6e7c54 | ||
|
|
e5d972cc2c | ||
|
|
7a43737cd6 | ||
|
|
4905e6fc7c | ||
|
|
fdd806e729 | ||
|
|
8a16c95be1 | ||
|
|
8248381150 | ||
|
|
0f6e9c7bfa | ||
|
|
d43c5c68bd | ||
|
|
31f8493ee3 | ||
|
|
8b57ca8c6c | ||
|
|
efa959895a | ||
|
|
36a29e826d | ||
|
|
7236e6ee02 | ||
|
|
50b9eddae9 | ||
|
|
7df2a57efb | ||
|
|
1c2caa09df | ||
|
|
4b366926d4 | ||
|
|
5e726a2af2 | ||
|
|
e2e3d110b7 | ||
|
|
deb904bbc4 | ||
|
|
09fd131f24 | ||
|
|
83c024dd66 | ||
|
|
c1eaf5fcab | ||
|
|
d09cf56e15 | ||
|
|
fbe3b5423d | ||
|
|
88bf8268f5 | ||
|
|
1c6d384f14 | ||
|
|
d7ab5c4d7b | ||
|
|
818fdc490c | ||
|
|
a5749a1392 | ||
|
|
922ff7f2bc | ||
|
|
da1e160add | ||
|
|
7e90c2c48f | ||
|
|
acb51d1702 | ||
|
|
890a737d1e | ||
|
|
94ff673d40 | ||
|
|
f6d5f6f79f | ||
|
|
8836b61aaa | ||
|
|
4f212dbaf9 | ||
|
|
fb139a7a01 | ||
|
|
754a2593f9 | ||
|
|
ae12f2e9d2 | ||
|
|
8d66ab742b | ||
|
|
ad79246376 | ||
|
|
13716f78aa | ||
|
|
584a82ea20 | ||
|
|
0dee4377b8 | ||
|
|
b94b193c21 | ||
|
|
479bbb240f | ||
|
|
814380b85c | ||
|
|
ea814ffa15 | ||
|
|
116ca090e0 | ||
|
|
ae19ff60cf |
0
.openchrome/hints/hints-2026-03-09.jsonl
Normal file
0
.openchrome/hints/hints-2026-03-09.jsonl
Normal file
0
.openchrome/timeline/timeline-2026-03-09.jsonl
Normal file
0
.openchrome/timeline/timeline-2026-03-09.jsonl
Normal file
14
AGENTS.md
14
AGENTS.md
@@ -1,10 +1,10 @@
|
||||
# oh-my-opencode — OpenCode Plugin
|
||||
|
||||
**Generated:** 2026-02-24 | **Commit:** fcb90d92 | **Branch:** dev
|
||||
**Generated:** 2026-03-02 | **Commit:** 1c2caa09 | **Branch:** dev
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
OpenCode plugin (npm: `oh-my-opencode`) that extends Claude Code (OpenCode fork) with multi-agent orchestration, 46 lifecycle hooks, 26 tools, skill/command/MCP systems, and Claude Code compatibility. 1208 TypeScript files, 143k LOC.
|
||||
OpenCode plugin (npm: `oh-my-opencode`) that extends Claude Code (OpenCode fork) with multi-agent orchestration, 46 lifecycle hooks, 26 tools, skill/command/MCP systems, and Claude Code compatibility. 1243 TypeScript files, 155k LOC.
|
||||
|
||||
## STRUCTURE
|
||||
|
||||
@@ -14,16 +14,16 @@ oh-my-opencode/
|
||||
│ ├── index.ts # Plugin entry: loadConfig → createManagers → createTools → createHooks → createPluginInterface
|
||||
│ ├── plugin-config.ts # JSONC multi-level config: user → project → defaults (Zod v4)
|
||||
│ ├── agents/ # 11 agents (Sisyphus, Hephaestus, Oracle, Librarian, Explore, Atlas, Prometheus, Metis, Momus, Multimodal-Looker, Sisyphus-Junior)
|
||||
| `hooks/` # 46 hooks across 39 directories + 6 standalone files
|
||||
│ ├── hooks/ # 46 hooks across 45 directories + 11 standalone files
|
||||
│ ├── tools/ # 26 tools across 15 directories
|
||||
│ ├── features/ # 19 feature modules (background-agent, skill-loader, tmux, MCP-OAuth, etc.)
|
||||
│ ├── shared/ # 100+ utility files in 13 categories
|
||||
│ ├── config/ # Zod v4 schema system (22+ files)
|
||||
│ ├── shared/ # 95+ utility files in 13 categories
|
||||
│ ├── config/ # Zod v4 schema system (24 files)
|
||||
│ ├── cli/ # CLI: install, run, doctor, mcp-oauth (Commander.js)
|
||||
│ ├── mcp/ # 3 built-in remote MCPs (websearch, context7, grep_app)
|
||||
│ ├── plugin/ # 8 OpenCode hook handlers + 46 hook composition
|
||||
│ └── plugin-handlers/ # 6-phase config loading pipeline
|
||||
├── packages/ # Monorepo: comment-checker, opencode-sdk, 10 platform binaries
|
||||
├── packages/ # Monorepo: cli-runner, 12 platform binaries
|
||||
└── local-ignore/ # Dev-only test fixtures
|
||||
```
|
||||
|
||||
@@ -123,7 +123,7 @@ bunx oh-my-opencode run # Non-interactive session
|
||||
|----------|---------|---------|
|
||||
| ci.yml | push/PR | Tests (split: mock-heavy isolated + batch), typecheck, build, schema auto-commit |
|
||||
| publish.yml | manual | Version bump, npm publish, platform binaries, GitHub release, merge to dev |
|
||||
| publish-platform.yml | called | 11 platform binaries via bun compile (darwin/linux/windows) |
|
||||
| publish-platform.yml | called | 12 platform binaries via bun compile (darwin/linux/windows) |
|
||||
| sisyphus-agent.yml | @mention | AI agent handles issues/PRs |
|
||||
|
||||
## NOTES
|
||||
|
||||
55
README.ja.md
55
README.ja.md
@@ -1,14 +1,3 @@
|
||||
> [!WARNING]
|
||||
> **セキュリティ警告: 偽装サイトにご注意ください**
|
||||
>
|
||||
> **ohmyopencode.com はこのプロジェクトとは一切関係がありません。** 私たちはそのサイトを運営したり承認したりしていません。
|
||||
>
|
||||
> OhMyOpenCodeは**無料かつオープンソース**です。「公式」を名乗る第三者のサイトからインストーラーをダウンロードしたり、支払い情報を入力したり**しないでください。**
|
||||
>
|
||||
> 偽装サイトはペイウォールの背後に隠れており、**どのような悪意あるプログラムを配布しているか検証できません**。そこからのダウンロードはすべて**潜在的に危険**であると見なしてください。
|
||||
>
|
||||
> ✅ 公式ダウンロード: https://github.com/code-yeongyu/oh-my-opencode/releases
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [](https://sisyphuslabs.ai)
|
||||
@@ -135,23 +124,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
- [GLM Coding プラン ($10)](https://z.ai/subscribe)
|
||||
- 従量課金(pay-per-token)の対象であれば、kimiやgeminiモデルを使っても費用はほとんどかかりません。
|
||||
|
||||
| | 機能 | 何をするのか |
|
||||
| :---: | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **規律あるエージェント (Discipline Agents)** | Sisyphusが Hephaestus、Oracle、Librarian、Exploreをオーケストレーションします。完全なAI開発チームが並列で動きます。 |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 一言でOK。すべてのエージェントがアクティブになり、終わるまで止まりません。 |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | ユーザーの真の意図を分析してから分類・行動します。もう文字通りに誤解して的外れなことをすることはありません。 |
|
||||
| 🔗 | **ハッシュベースの編集ツール** | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-lineエラー0%。[oh-my-pi](https://github.com/can1357/oh-my-pi)にインスパイアされています。[ハーネス問題 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | ワークスペース単位のリネーム、ビルド前の診断、ASTを考慮した書き換え。エージェントにIDEレベルの精度を提供します。 |
|
||||
| 🧠 | **バックグラウンドエージェント** | 5人以上の専門家を並列で投入します。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
|
||||
| 📚 | **組み込みMCP** | Exa(Web検索)、Context7(公式ドキュメント)、Grep.app(GitHub検索)。常にオンです。 |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自己参照ループ。100%完了するまで絶対に止まりません。 |
|
||||
| ✅ | **Todoの強制執行** | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
|
||||
| 💬 | **コメントチェッカー** | コメントからAI臭い無駄話を排除します。シニアエンジニアが書いたようなコードになります。 |
|
||||
| 🖥️ | **Tmux統合** | 完全なインタラクティブターミナル。REPL、デバッガー、TUIアプリがすべてリアルタイムで動きます。 |
|
||||
| 🔌 | **Claude Code互換性** | 既存のフック、コマンド、スキル、MCP、プラグイン?すべてここでそのまま動きます。 |
|
||||
| 🎯 | **スキル内蔵MCP** | スキルが独自のMCPサーバーを持ち歩きます。コンテキストが肥大化しません。 |
|
||||
| 📋 | **Prometheusプランナー** | インタビューモードで、コードを1行触る前に戦略的な計画から立てます。 |
|
||||
| 🔍 | **`/init-deep`** | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成します。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
|
||||
| | 機能 | 何をするのか |
|
||||
| :---: | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **規律あるエージェント (Discipline Agents)** | Sisyphusが Hephaestus、Oracle、Librarian、Exploreをオーケストレーションします。完全なAI開発チームが並列で動きます。 |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 一言でOK。すべてのエージェントがアクティブになり、終わるまで止まりません。 |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | ユーザーの真の意図を分析してから分類・行動します。もう文字通りに誤解して的外れなことをすることはありません。 |
|
||||
| 🔗 | **ハッシュベースの編集ツール** | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-lineエラー0%。[oh-my-pi](https://github.com/can1357/oh-my-pi)にインスパイアされています。[ハーネス問題 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | ワークスペース単位のリネーム、ビルド前の診断、ASTを考慮した書き換え。エージェントにIDEレベルの精度を提供します。 |
|
||||
| 🧠 | **バックグラウンドエージェント** | 5人以上の専門家を並列で投入します。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
|
||||
| 📚 | **組み込みMCP** | Exa(Web検索)、Context7(公式ドキュメント)、Grep.app(GitHub検索)。常にオンです。 |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自己参照ループ。100%完了するまで絶対に止まりません。 |
|
||||
| ✅ | **Todoの強制執行** | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
|
||||
| 💬 | **コメントチェッカー** | コメントからAI臭い無駄話を排除します。シニアエンジニアが書いたようなコードになります。 |
|
||||
| 🖥️ | **Tmux統合** | 完全なインタラクティブターミナル。REPL、デバッガー、TUIアプリがすべてリアルタイムで動きます。 |
|
||||
| 🔌 | **Claude Code互換性** | 既存のフック、コマンド、スキル、MCP、プラグイン?すべてここでそのまま動きます。 |
|
||||
| 🎯 | **スキル内蔵MCP** | スキルが独自のMCPサーバーを持ち歩きます。コンテキストが肥大化しません。 |
|
||||
| 📋 | **Prometheusプランナー** | インタビューモードで、コードを1行触る前に戦略的な計画から立てます。 |
|
||||
| 🔍 | **`/init-deep`** | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成します。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
|
||||
|
||||
### 規律あるエージェント (Discipline Agents)
|
||||
|
||||
@@ -176,11 +165,11 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
|
||||
Sisyphusがサブエージェントにタスクを委任する際、モデルを直接選ぶことはありません。**カテゴリー**を選びます。カテゴリーは自動的に適切なモデルにマッピングされます:
|
||||
|
||||
| カテゴリー | 用途 |
|
||||
| :------------------- | :--------------------------------- |
|
||||
| `visual-engineering` | フロントエンド、UI/UX、デザイン |
|
||||
| `deep` | 自律的なリサーチと実行 |
|
||||
| `quick` | 単一ファイルの変更、タイポの修正 |
|
||||
| カテゴリー | 用途 |
|
||||
| :------------------- | :----------------------------------- |
|
||||
| `visual-engineering` | フロントエンド、UI/UX、デザイン |
|
||||
| `deep` | 自律的なリサーチと実行 |
|
||||
| `quick` | 単一ファイルの変更、タイポの修正 |
|
||||
| `ultrabrain` | ハードロジック、アーキテクチャの決定 |
|
||||
|
||||
エージェントがどのような種類の作業かを伝え、ハーネスが適切なモデルを選択します。あなたは何も触る必要はありません。
|
||||
|
||||
60
README.ko.md
60
README.ko.md
@@ -1,19 +1,3 @@
|
||||
> [!WARNING]
|
||||
> **보안 경고: 사칭 사이트 주의**
|
||||
>
|
||||
> **ohmyopencode.com은 이 프로젝트와 아무런 관련이 없습니다.** 우리는 해당 사이트를 운영하거나 보증하지 않습니다.
|
||||
>
|
||||
> OhMyOpenCode는 **무료 오픈소스**입니다. "공식"을 사칭하는 제3자 사이트에서 인스톨러를 다운로드하거나 결제 정보를 입력하지 **마세요.**
|
||||
>
|
||||
> 사칭 사이트는 페이월 뒤에 숨어 있어 **어떤 악성 코드를 배포하는지 확인할 수 없습니다**. 해당 사이트의 다운로드는 모두 **잠재적 위험**으로 간주하세요.
|
||||
>
|
||||
> ✅ 공식 다운로드: https://github.com/code-yeongyu/oh-my-opencode/releases
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [](https://sisyphuslabs.ai)
|
||||
> > **우리는 프론티어 에이전트의 미래를 정의하기 위해 Sisyphus의 완벽한 프로덕트 버전을 만들고 있습니다. <br />[여기](https://sisyphuslabs.ai)에서 대기자 명단에 등록하세요.**
|
||||
|
||||
> [!TIP]
|
||||
> 저희와 함께 하세요!
|
||||
>
|
||||
@@ -134,23 +118,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
- [GLM Coding 요금제 ($10)](https://z.ai/subscribe)
|
||||
- 종량제(pay-per-token) 대상자라면 kimi와 gemini 모델을 써도 비용이 별로 안 나옵니다.
|
||||
|
||||
| | 기능 | 역할 |
|
||||
| :---: | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **기강 잡힌 에이전트 (Discipline Agents)** | Sisyphus가 Hephaestus, Oracle, Librarian, Explore를 오케스트레이션합니다. 완전한 AI 개발팀이 병렬로 돌아갑니다. |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 단어 하나면 됩니다. 모든 에이전트가 활성화되고 다 끝날 때까지 멈추지 않습니다. |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | 사용자의 진짜 의도를 분석한 뒤 분류하거나 행동합니다. 더 이상 문자 그대로 오해해서 헛짓거리하는 일이 없습니다. |
|
||||
| 🔗 | **해시 기반 편집 툴** | `LINE#ID` 콘텐츠 해시로 모든 변경 사항을 검증합니다. stale-line 에러 0%. [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감을 받았습니다. [하니스 프로블러 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | 워크스페이스 단위 이름 변경, 빌드 전 진단, AST 기반 재작성. 에이전트에게 IDE급 정밀도를 제공합니다. |
|
||||
| 🧠 | **백그라운드 에이전트** | 5명 이상의 전문가를 병렬로 투입합니다. 컨텍스트는 가볍게 유지하고 결과는 준비될 때 받습니다. |
|
||||
| 📚 | **기본 내장 MCP** | Exa(웹 검색), Context7(공식 문서), Grep.app(GitHub 검색). 항상 켜져 있습니다. |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 자기 참조 루프. 100% 완료될 때까지 절대 멈추지 않습니다. |
|
||||
| ✅ | **Todo 강제 집행** | 에이전트가 딴짓한다고요? 시스템이 멱살 잡고 끌고 옵니다. 당신의 작업은 무조건 끝납니다. |
|
||||
| 💬 | **주석 검사기** | 주석에 AI 냄새나는 헛소리를 빼버립니다. 시니어 개발자가 짠 것 같은 코드가 됩니다. |
|
||||
| 🖥️ | **Tmux 연동** | 완전한 인터랙티브 터미널. REPL, 디버거, TUI 앱들 모두 실시간으로 돌아갑니다. |
|
||||
| 🔌 | **Claude Code 호환성** | 기존 훅, 명령어, 스킬, MCP, 플러그인? 전부 여기서 그대로 돌아갑니다. |
|
||||
| 🎯 | **스킬 내장 MCP** | 스킬이 자기만의 MCP 서버를 들고 다닙니다. 컨텍스트가 부풀어 오르지 않습니다. |
|
||||
| 📋 | **Prometheus 플래너** | 인터뷰 모드로 코드 한 줄 만지기 전에 전략적인 계획부터 세웁니다. |
|
||||
| 🔍 | **`/init-deep`** | 프로젝트 전체에 걸쳐 계층적인 `AGENTS.md` 파일을 자동 생성합니다. 토큰 효율과 에이전트 성능 둘 다 잡습니다. |
|
||||
| | 기능 | 역할 |
|
||||
| :---: | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **기강 잡힌 에이전트 (Discipline Agents)** | Sisyphus가 Hephaestus, Oracle, Librarian, Explore를 오케스트레이션합니다. 완전한 AI 개발팀이 병렬로 돌아갑니다. |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 단어 하나면 됩니다. 모든 에이전트가 활성화되고 다 끝날 때까지 멈추지 않습니다. |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | 사용자의 진짜 의도를 분석한 뒤 분류하거나 행동합니다. 더 이상 문자 그대로 오해해서 헛짓거리하는 일이 없습니다. |
|
||||
| 🔗 | **해시 기반 편집 툴** | `LINE#ID` 콘텐츠 해시로 모든 변경 사항을 검증합니다. stale-line 에러 0%. [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감을 받았습니다. [하니스 프로블러 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | 워크스페이스 단위 이름 변경, 빌드 전 진단, AST 기반 재작성. 에이전트에게 IDE급 정밀도를 제공합니다. |
|
||||
| 🧠 | **백그라운드 에이전트** | 5명 이상의 전문가를 병렬로 투입합니다. 컨텍스트는 가볍게 유지하고 결과는 준비될 때 받습니다. |
|
||||
| 📚 | **기본 내장 MCP** | Exa(웹 검색), Context7(공식 문서), Grep.app(GitHub 검색). 항상 켜져 있습니다. |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 자기 참조 루프. 100% 완료될 때까지 절대 멈추지 않습니다. |
|
||||
| ✅ | **Todo 강제 집행** | 에이전트가 딴짓한다고요? 시스템이 멱살 잡고 끌고 옵니다. 당신의 작업은 무조건 끝납니다. |
|
||||
| 💬 | **주석 검사기** | 주석에 AI 냄새나는 헛소리를 빼버립니다. 시니어 개발자가 짠 것 같은 코드가 됩니다. |
|
||||
| 🖥️ | **Tmux 연동** | 완전한 인터랙티브 터미널. REPL, 디버거, TUI 앱들 모두 실시간으로 돌아갑니다. |
|
||||
| 🔌 | **Claude Code 호환성** | 기존 훅, 명령어, 스킬, MCP, 플러그인? 전부 여기서 그대로 돌아갑니다. |
|
||||
| 🎯 | **스킬 내장 MCP** | 스킬이 자기만의 MCP 서버를 들고 다닙니다. 컨텍스트가 부풀어 오르지 않습니다. |
|
||||
| 📋 | **Prometheus 플래너** | 인터뷰 모드로 코드 한 줄 만지기 전에 전략적인 계획부터 세웁니다. |
|
||||
| 🔍 | **`/init-deep`** | 프로젝트 전체에 걸쳐 계층적인 `AGENTS.md` 파일을 자동 생성합니다. 토큰 효율과 에이전트 성능 둘 다 잡습니다. |
|
||||
|
||||
### 기강 잡힌 에이전트 (Discipline Agents)
|
||||
|
||||
@@ -176,11 +160,11 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
Sisyphus가 하위 에이전트에게 일을 맡길 때, 모델을 직접 고르지 않습니다. **카테고리**를 고릅니다. 카테고리는 자동으로 올바른 모델에 매핑됩니다:
|
||||
|
||||
| 카테고리 | 용도 |
|
||||
| :------------------- | :--------------------------------- |
|
||||
| `visual-engineering` | 프론트엔드, UI/UX, 디자인 |
|
||||
| `deep` | 자율 리서치 및 실행 |
|
||||
| `quick` | 단일 파일 변경, 오타 수정 |
|
||||
| `ultrabrain` | 하드 로직, 아키텍처 결정 |
|
||||
| :------------------- | :------------------------ |
|
||||
| `visual-engineering` | 프론트엔드, UI/UX, 디자인 |
|
||||
| `deep` | 자율 리서치 및 실행 |
|
||||
| `quick` | 단일 파일 변경, 오타 수정 |
|
||||
| `ultrabrain` | 하드 로직, 아키텍처 결정 |
|
||||
|
||||
에이전트가 어떤 작업인지 말하면, 하네스가 알아서 적합한 모델을 꺼내옵니다. 당신은 손댈 게 없습니다.
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -1,14 +1,3 @@
|
||||
> [!WARNING]
|
||||
> **Security warning: impersonation site**
|
||||
>
|
||||
> **ohmyopencode.com is NOT affiliated with this project.** We do not operate or endorse that site.
|
||||
>
|
||||
> OhMyOpenCode is **free and open-source**. Do **not** download installers or enter payment details on third-party sites that claim to be "official."
|
||||
>
|
||||
> Because the impersonation site is behind a paywall, we **cannot verify what it distributes**. Treat any downloads from it as **potentially unsafe**.
|
||||
>
|
||||
> ✅ Official downloads: https://github.com/code-yeongyu/oh-my-opencode/releases
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [](https://sisyphuslabs.ai)
|
||||
@@ -134,7 +123,7 @@ Everything below, every feature, every optimization, you don't need to know it.
|
||||
|
||||
Even only with following subscriptions, ultrawork will work well (this project is not affiliated, this is just personal recommendation):
|
||||
- [ChatGPT Subscription ($20)](https://chatgpt.com/)
|
||||
- [Kimi Code Subscription ($0.99) (*only this month)](https://www.kimi.com/membership/pricing?track_id=5cdeca93-66f0-4d35-aabb-b6df8fcea328)
|
||||
- [Kimi Code Subscription ($0.99) (*only this month)](https://www.kimi.com/kimiplus/sale)
|
||||
- [GLM Coding Plan ($10)](https://z.ai/subscribe)
|
||||
- If you are eligible for pay-per-token, using kimi and gemini models won't cost you that much.
|
||||
|
||||
|
||||
46
README.ru.md
46
README.ru.md
@@ -1,13 +1,3 @@
|
||||
> [!WARNING] **Предупреждение о безопасности: сайт-имитатор**
|
||||
>
|
||||
> **ohmyopencode.com НЕ аффилирован с этим проектом.** Мы не управляем этим сайтом и не одобряем его.
|
||||
>
|
||||
> OhMyOpenCode — **бесплатный и открытый проект**. Не скачивайте установщики и не вводите платёжные данные на сторонних сайтах, которые называют себя «официальными».
|
||||
>
|
||||
> Поскольку сайт-имитатор находится за платным доступом, мы **не можем проверить, что именно он распространяет**. Относитесь к любым загрузкам с него как к **потенциально небезопасным**.
|
||||
>
|
||||
> ✅ Официальные загрузки: https://github.com/code-yeongyu/oh-my-opencode/releases
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [](https://sisyphuslabs.ai)
|
||||
@@ -121,23 +111,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
- [Тариф GLM Coding ($10)](https://z.ai/subscribe)
|
||||
- При доступе к оплате за токены использование моделей Kimi и Gemini обойдётся недорого.
|
||||
|
||||
| | Функция | Что делает |
|
||||
| ---- | -------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| 🤖 | **Дисциплинированные агенты** | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | Одно слово. Все агенты активируются. Не останавливается, пока задача не выполнена. |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Анализирует истинное намерение пользователя перед классификацией и действием. Никакого буквального неверного толкования. |
|
||||
| 🔗 | **Инструмент правок на основе хэш-якорей** | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [Проблема обвязки →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. |
|
||||
| 🧠 | **Фоновые агенты** | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
|
||||
| 📚 | **Встроенные MCP** | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
|
||||
| ✅ | **Todo Enforcer** | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
|
||||
| 💬 | **Comment Checker** | Никакого AI-мусора в комментариях. Код читается так, словно его писал опытный разработчик. |
|
||||
| 🖥️ | **Интеграция с Tmux** | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
|
||||
| 🔌 | **Совместимость с Claude Code** | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. |
|
||||
| 🎯 | **MCP, встроенные в навыки** | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
|
||||
| 📋 | **Prometheus Planner** | Стратегическое планирование в режиме интервью перед любым выполнением. |
|
||||
| 🔍 | **`/init-deep`** | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
|
||||
| | Функция | Что делает |
|
||||
| --- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **Дисциплинированные агенты** | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | Одно слово. Все агенты активируются. Не останавливается, пока задача не выполнена. |
|
||||
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Анализирует истинное намерение пользователя перед классификацией и действием. Никакого буквального неверного толкования. |
|
||||
| 🔗 | **Инструмент правок на основе хэш-якорей** | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [Проблема обвязки →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. |
|
||||
| 🧠 | **Фоновые агенты** | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
|
||||
| 📚 | **Встроенные MCP** | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
|
||||
| ✅ | **Todo Enforcer** | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
|
||||
| 💬 | **Comment Checker** | Никакого AI-мусора в комментариях. Код читается так, словно его писал опытный разработчик. |
|
||||
| 🖥️ | **Интеграция с Tmux** | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
|
||||
| 🔌 | **Совместимость с Claude Code** | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. |
|
||||
| 🎯 | **MCP, встроенные в навыки** | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
|
||||
| 📋 | **Prometheus Planner** | Стратегическое планирование в режиме интервью перед любым выполнением. |
|
||||
| 🔍 | **`/init-deep`** | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
|
||||
|
||||
### Дисциплинированные агенты
|
||||
|
||||
@@ -268,7 +258,7 @@ project/
|
||||
```bash
|
||||
# Удалить пользовательский конфиг
|
||||
rm -f ~/.config/opencode/oh-my-opencode.json ~/.config/opencode/oh-my-opencode.jsonc
|
||||
|
||||
|
||||
# Удалить конфиг проекта (если существует)
|
||||
rm -f .opencode/oh-my-opencode.json .opencode/oh-my-opencode.jsonc
|
||||
```
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
> [!WARNING]
|
||||
> **安全警告:注意假冒网站**
|
||||
>
|
||||
> **ohmyopencode.com 与本项目没有任何关系。** 我们不运营也不认可该网站。
|
||||
>
|
||||
> OhMyOpenCode 是**免费且开源的**。**不要**从自称“官方”的第三方网站下载安装程序或输入付款信息。
|
||||
>
|
||||
> 假冒网站隐藏在付费墙后,我们**无法验证它分发的内容**。将其所有下载视为**潜在危险**。
|
||||
>
|
||||
> ✅ 官方下载地址:https://github.com/code-yeongyu/oh-my-opencode/releases
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [](https://sisyphuslabs.ai)
|
||||
@@ -136,23 +125,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
- [GLM Coding 套餐 ($10)](https://z.ai/subscribe)
|
||||
- 如果你能使用按 token 计费的方式,用 kimi 和 gemini 模型花不了多少钱。
|
||||
|
||||
| | 特性 | 功能说明 |
|
||||
| :---: | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 🤖 | **自律军团 (Discipline Agents)** | Sisyphus 负责调度 Hephaestus、Oracle、Librarian 和 Explore。一支完整的 AI 开发团队并行工作。 |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 一键触发,所有智能体出动。任务完成前绝不罢休。 |
|
||||
| 🚪 | **[IntentGate 意图门](https://factory.ai/news/terminal-bench)** | 真正行动前,先分析用户的真实意图。彻底告别被字面意思误导的 AI 废话。 |
|
||||
| 🔗 | **基于哈希的编辑工具** | 每次修改都通过 `LINE#ID` 内容哈希验证、0% 错误修改。灵感来自 [oh-my-pi](https://github.com/can1357/oh-my-pi)。[马具问题 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | 工作区级别的重命名、构建前诊断、基于 AST 的重写。为 Agent 提供 IDE 级别的精度。 |
|
||||
| 🧠 | **后台智能体** | 同时发射 5+ 个专家并行工作。保持上下文干净,随时获取成果。 |
|
||||
| 📚 | **内置 MCP** | Exa (网络搜索)、Context7 (官方文档)、Grep.app (GitHub 源码搜索)。默认开启。 |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自我引用闭环。达不到 100% 完成度绝不停止。 |
|
||||
| ✅ | **Todo 强制执行** | Agent 想要摸鱼?系统直接揪着领子拽回来。你的任务,必须完成。 |
|
||||
| 💬 | **注释审查员** | 剔除带有浓烈 AI 味的冗余注释。写出的代码就像老练的高级工程师写的。 |
|
||||
| 🖥️ | **Tmux 集成** | 完整的交互式终端支持。跑 REPL、用调试器、用 TUI 工具,全都在实时会话中完成。 |
|
||||
| 🔌 | **Claude Code 兼容** | 你现有的 Hooks、命令、技能、MCP 和插件?全都能无缝迁移过来。 |
|
||||
| 🎯 | **技能内嵌 MCP** | 技能自带其所需的 MCP 服务器。按需开启,不会撑爆你的上下文窗口。 |
|
||||
| 📋 | **Prometheus 规划师** | 动手写代码前,先通过访谈模式做好战略规划。 |
|
||||
| 🔍 | **`/init-deep`** | 在整个项目目录层级中自动生成 `AGENTS.md`。不仅省 Token,还能大幅提升 Agent 理解力。 |
|
||||
| | 特性 | 功能说明 |
|
||||
| :---: | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 🤖 | **自律军团 (Discipline Agents)** | Sisyphus 负责调度 Hephaestus、Oracle、Librarian 和 Explore。一支完整的 AI 开发团队并行工作。 |
|
||||
| ⚡ | **`ultrawork` / `ulw`** | 一键触发,所有智能体出动。任务完成前绝不罢休。 |
|
||||
| 🚪 | **[IntentGate 意图门](https://factory.ai/news/terminal-bench)** | 真正行动前,先分析用户的真实意图。彻底告别被字面意思误导的 AI 废话。 |
|
||||
| 🔗 | **基于哈希的编辑工具** | 每次修改都通过 `LINE#ID` 内容哈希验证、0% 错误修改。灵感来自 [oh-my-pi](https://github.com/can1357/oh-my-pi)。[马具问题 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
|
||||
| 🛠️ | **LSP + AST-Grep** | 工作区级别的重命名、构建前诊断、基于 AST 的重写。为 Agent 提供 IDE 级别的精度。 |
|
||||
| 🧠 | **后台智能体** | 同时发射 5+ 个专家并行工作。保持上下文干净,随时获取成果。 |
|
||||
| 📚 | **内置 MCP** | Exa (网络搜索)、Context7 (官方文档)、Grep.app (GitHub 源码搜索)。默认开启。 |
|
||||
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自我引用闭环。达不到 100% 完成度绝不停止。 |
|
||||
| ✅ | **Todo 强制执行** | Agent 想要摸鱼?系统直接揪着领子拽回来。你的任务,必须完成。 |
|
||||
| 💬 | **注释审查员** | 剔除带有浓烈 AI 味的冗余注释。写出的代码就像老练的高级工程师写的。 |
|
||||
| 🖥️ | **Tmux 集成** | 完整的交互式终端支持。跑 REPL、用调试器、用 TUI 工具,全都在实时会话中完成。 |
|
||||
| 🔌 | **Claude Code 兼容** | 你现有的 Hooks、命令、技能、MCP 和插件?全都能无缝迁移过来。 |
|
||||
| 🎯 | **技能内嵌 MCP** | 技能自带其所需的 MCP 服务器。按需开启,不会撑爆你的上下文窗口。 |
|
||||
| 📋 | **Prometheus 规划师** | 动手写代码前,先通过访谈模式做好战略规划。 |
|
||||
| 🔍 | **`/init-deep`** | 在整个项目目录层级中自动生成 `AGENTS.md`。不仅省 Token,还能大幅提升 Agent 理解力。 |
|
||||
|
||||
### 自律军团 (Discipline Agents)
|
||||
|
||||
@@ -177,11 +166,11 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
|
||||
|
||||
当 Sisyphus 把任务分配给子智能体时,他选择的不是具体的模型,而是 **类别 (Category)**。系统会自动将类别映射到最合适的模型:
|
||||
|
||||
| 类别 | 作用领域 |
|
||||
| :------------------- | :--------------------------------- |
|
||||
| `visual-engineering` | 前端、UI/UX、设计 |
|
||||
| `deep` | 深度自主调研与执行 |
|
||||
| `quick` | 单文件修改、修错字 |
|
||||
| 类别 | 作用领域 |
|
||||
| :------------------- | :--------------------- |
|
||||
| `visual-engineering` | 前端、UI/UX、设计 |
|
||||
| `deep` | 深度自主调研与执行 |
|
||||
| `quick` | 单文件修改、修错字 |
|
||||
| `ultrabrain` | 复杂硬核逻辑、架构决策 |
|
||||
|
||||
智能体只需要说明要做什么类型的工作,框架就会挑选出最合适的模型去干。你完全不需要操心。
|
||||
|
||||
@@ -3685,6 +3685,10 @@
|
||||
"messageStalenessTimeoutMs": {
|
||||
"type": "number",
|
||||
"minimum": 60000
|
||||
},
|
||||
"syncPollTimeoutMs": {
|
||||
"type": "number",
|
||||
"minimum": 60000
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -3837,6 +3841,19 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"start_work": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auto_commit": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"auto_commit"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"_migrations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
119
bun.lock
119
bun.lock
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "oh-my-opencode",
|
||||
@@ -8,10 +8,10 @@
|
||||
"@ast-grep/cli": "^0.40.0",
|
||||
"@ast-grep/napi": "^0.40.0",
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"@code-yeongyu/comment-checker": "^0.6.1",
|
||||
"@code-yeongyu/comment-checker": "^0.7.0",
|
||||
"@modelcontextprotocol/sdk": "^1.25.2",
|
||||
"@opencode-ai/plugin": "^1.1.19",
|
||||
"@opencode-ai/sdk": "^1.1.19",
|
||||
"@opencode-ai/plugin": "^1.2.16",
|
||||
"@opencode-ai/sdk": "^1.2.17",
|
||||
"commander": "^14.0.2",
|
||||
"detect-libc": "^2.0.0",
|
||||
"diff": "^8.0.3",
|
||||
@@ -29,13 +29,17 @@
|
||||
"typescript": "^5.7.3",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"oh-my-opencode-darwin-arm64": "3.8.5",
|
||||
"oh-my-opencode-darwin-x64": "3.8.5",
|
||||
"oh-my-opencode-linux-arm64": "3.8.5",
|
||||
"oh-my-opencode-linux-arm64-musl": "3.8.5",
|
||||
"oh-my-opencode-linux-x64": "3.8.5",
|
||||
"oh-my-opencode-linux-x64-musl": "3.8.5",
|
||||
"oh-my-opencode-windows-x64": "3.8.5",
|
||||
"oh-my-opencode-darwin-arm64": "3.10.0",
|
||||
"oh-my-opencode-darwin-x64": "3.10.0",
|
||||
"oh-my-opencode-darwin-x64-baseline": "3.10.0",
|
||||
"oh-my-opencode-linux-arm64": "3.10.0",
|
||||
"oh-my-opencode-linux-arm64-musl": "3.10.0",
|
||||
"oh-my-opencode-linux-x64": "3.10.0",
|
||||
"oh-my-opencode-linux-x64-baseline": "3.10.0",
|
||||
"oh-my-opencode-linux-x64-musl": "3.10.0",
|
||||
"oh-my-opencode-linux-x64-musl-baseline": "3.10.0",
|
||||
"oh-my-opencode-windows-x64": "3.10.0",
|
||||
"oh-my-opencode-windows-x64-baseline": "3.10.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -44,72 +48,75 @@
|
||||
"@ast-grep/napi",
|
||||
"@code-yeongyu/comment-checker",
|
||||
],
|
||||
"overrides": {
|
||||
"@opencode-ai/sdk": "^1.2.17",
|
||||
},
|
||||
"packages": {
|
||||
"@ast-grep/cli": ["@ast-grep/cli@0.40.0", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "@ast-grep/cli-darwin-arm64": "0.40.0", "@ast-grep/cli-darwin-x64": "0.40.0", "@ast-grep/cli-linux-arm64-gnu": "0.40.0", "@ast-grep/cli-linux-x64-gnu": "0.40.0", "@ast-grep/cli-win32-arm64-msvc": "0.40.0", "@ast-grep/cli-win32-ia32-msvc": "0.40.0", "@ast-grep/cli-win32-x64-msvc": "0.40.0" }, "bin": { "sg": "sg", "ast-grep": "ast-grep" } }, "sha512-L8AkflsfI2ZP70yIdrwqvjR02ScCuRmM/qNGnJWUkOFck+e6gafNVJ4e4jjGQlEul+dNdBpx36+O2Op629t47A=="],
|
||||
"@ast-grep/cli": ["@ast-grep/cli@0.40.5", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "@ast-grep/cli-darwin-arm64": "0.40.5", "@ast-grep/cli-darwin-x64": "0.40.5", "@ast-grep/cli-linux-arm64-gnu": "0.40.5", "@ast-grep/cli-linux-x64-gnu": "0.40.5", "@ast-grep/cli-win32-arm64-msvc": "0.40.5", "@ast-grep/cli-win32-ia32-msvc": "0.40.5", "@ast-grep/cli-win32-x64-msvc": "0.40.5" }, "bin": { "sg": "sg", "ast-grep": "ast-grep" } }, "sha512-yVXL7Gz0WIHerQLf+MVaVSkhIhidtWReG5akNVr/JS9OVCVkSdz7gWm7H8jVv2M9OO1tauuG76K3UaRGBPu5lQ=="],
|
||||
|
||||
"@ast-grep/cli-darwin-arm64": ["@ast-grep/cli-darwin-arm64@0.40.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UehY2MMUkdJbsriP7NKc6+uojrqPn7d1Cl0em+WAkee7Eij81VdyIjRsRxtZSLh440ZWQBHI3PALZ9RkOO8pKQ=="],
|
||||
"@ast-grep/cli-darwin-arm64": ["@ast-grep/cli-darwin-arm64@0.40.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-T9CzwJ1GqQhnANdsu6c7iT1akpvTVMK+AZrxnhIPv33Ze5hrXUUkqan+j4wUAukRJDqU7u94EhXLSLD+5tcJ8g=="],
|
||||
|
||||
"@ast-grep/cli-darwin-x64": ["@ast-grep/cli-darwin-x64@0.40.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-RFDJ2ZxUbT0+grntNlOLJx7wa9/ciVCeaVtQpQy8WJJTvXvkY0etl8Qlh2TmO2x2yr+i0Z6aMJi4IG/Yx5ghTQ=="],
|
||||
"@ast-grep/cli-darwin-x64": ["@ast-grep/cli-darwin-x64@0.40.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-ez9b2zKvXU8f4ghhjlqYvbx6tWCKJTuVlNVqDDfjqwwhGeiTYfnzMlSVat4ElYRMd21gLtXZIMy055v2f21Ztg=="],
|
||||
|
||||
"@ast-grep/cli-linux-arm64-gnu": ["@ast-grep/cli-linux-arm64-gnu@0.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4p55gnTQ1mMFCyqjtM7bH9SB9r16mkwXtUcJQGX1YgFG4WD+QG8rC4GwSuNNZcdlYaOQuTWrgUEQ9z5K06UXfg=="],
|
||||
"@ast-grep/cli-linux-arm64-gnu": ["@ast-grep/cli-linux-arm64-gnu@0.40.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-VXa2L1IEYD66AMb0GuG7VlMMbPmEGoJUySWDcwSZo/D9neiry3MJ41LQR5oTG2HyhIPBsf9umrXnmuRq66BviA=="],
|
||||
|
||||
"@ast-grep/cli-linux-x64-gnu": ["@ast-grep/cli-linux-x64-gnu@0.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-u2MXFceuwvrO+OQ6zFGoJ6wbATXn46HWwW79j4UPrXYJzVl97jRyjJOIQTJOzTflsk02fjP98DQkfvbXt2dl3Q=="],
|
||||
"@ast-grep/cli-linux-x64-gnu": ["@ast-grep/cli-linux-x64-gnu@0.40.5", "", { "os": "linux", "cpu": "x64" }, "sha512-GQC5162eIOWXR2eQQ6Knzg7/8Trp5E1ODJkaErf0IubdQrZBGqj5AAcQPcWgPbbnmktjIp0H4NraPpOJ9eJ22A=="],
|
||||
|
||||
"@ast-grep/cli-win32-arm64-msvc": ["@ast-grep/cli-win32-arm64-msvc@0.40.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-E/I1xpF/RQL2fo1CQsQfTxyDLnChsbZ+ERrQHKuF1FI4WrkaPOBibpqda60QgVmUcgOGZyZ/GRb3iKEVWPsQNQ=="],
|
||||
"@ast-grep/cli-win32-arm64-msvc": ["@ast-grep/cli-win32-arm64-msvc@0.40.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-YiZdnQZsSlXQTMsZJop/Ux9MmUGfuRvC2x/UbFgrt5OBSYxND+yoiMc0WcA3WG+wU+tt4ZkB5HUea3r/IkOLYA=="],
|
||||
|
||||
"@ast-grep/cli-win32-ia32-msvc": ["@ast-grep/cli-win32-ia32-msvc@0.40.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-9h12OQu1BR0GxHEtT+Z4QkJk3LLWLiKwjBkjXUGlASHYDPTyLcs85KwDLeFHs4BwarF8TDdF+KySvB9WPGl/nQ=="],
|
||||
"@ast-grep/cli-win32-ia32-msvc": ["@ast-grep/cli-win32-ia32-msvc@0.40.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-MHkCxCITVTr8sY9CcVqNKbfUzMa3Hc6IilGXad0Clnw2vNmPfWqSky+hU/UTerr5YHWwWfAVURH7ANZgirtx0Q=="],
|
||||
|
||||
"@ast-grep/cli-win32-x64-msvc": ["@ast-grep/cli-win32-x64-msvc@0.40.0", "", { "os": "win32", "cpu": "x64" }, "sha512-n2+3WynEWFHhXg6KDgjwWQ0UEtIvqUITFbKEk5cDkUYrzYhg/A6kj0qauPwRbVMoJms49vtsNpLkzzqyunio5g=="],
|
||||
"@ast-grep/cli-win32-x64-msvc": ["@ast-grep/cli-win32-x64-msvc@0.40.5", "", { "os": "win32", "cpu": "x64" }, "sha512-/MJ5un7yxlClaaxou9eYl+Kr2xr/yTtYtTq5aLBWjPWA6dmmJ1nAJgx5zKHVuplFXFBrFDQk3paEgAETMTGcrA=="],
|
||||
|
||||
"@ast-grep/napi": ["@ast-grep/napi@0.40.0", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.40.0", "@ast-grep/napi-darwin-x64": "0.40.0", "@ast-grep/napi-linux-arm64-gnu": "0.40.0", "@ast-grep/napi-linux-arm64-musl": "0.40.0", "@ast-grep/napi-linux-x64-gnu": "0.40.0", "@ast-grep/napi-linux-x64-musl": "0.40.0", "@ast-grep/napi-win32-arm64-msvc": "0.40.0", "@ast-grep/napi-win32-ia32-msvc": "0.40.0", "@ast-grep/napi-win32-x64-msvc": "0.40.0" } }, "sha512-tq6nO/8KwUF/mHuk1ECaAOSOlz2OB/PmygnvprJzyAHGRVzdcffblaOOWe90M9sGz5MAasXoF+PTcayQj9TKKA=="],
|
||||
"@ast-grep/napi": ["@ast-grep/napi@0.40.5", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.40.5", "@ast-grep/napi-darwin-x64": "0.40.5", "@ast-grep/napi-linux-arm64-gnu": "0.40.5", "@ast-grep/napi-linux-arm64-musl": "0.40.5", "@ast-grep/napi-linux-x64-gnu": "0.40.5", "@ast-grep/napi-linux-x64-musl": "0.40.5", "@ast-grep/napi-win32-arm64-msvc": "0.40.5", "@ast-grep/napi-win32-ia32-msvc": "0.40.5", "@ast-grep/napi-win32-x64-msvc": "0.40.5" } }, "sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A=="],
|
||||
|
||||
"@ast-grep/napi-darwin-arm64": ["@ast-grep/napi-darwin-arm64@0.40.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZMjl5yLhKjxdwbqEEdMizgQdWH2NrWsM6Px+JuGErgCDe6Aedq9yurEPV7veybGdLVJQhOah6htlSflXxjHnYA=="],
|
||||
"@ast-grep/napi-darwin-arm64": ["@ast-grep/napi-darwin-arm64@0.40.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ=="],
|
||||
|
||||
"@ast-grep/napi-darwin-x64": ["@ast-grep/napi-darwin-x64@0.40.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-f9Ol5oQKNRMBkvDtzBK1WiNn2/3eejF2Pn9xwTj7PhXuSFseedOspPYllxQo0gbwUlw/DJqGFTce/jarhR/rBw=="],
|
||||
"@ast-grep/napi-darwin-x64": ["@ast-grep/napi-darwin-x64@0.40.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA=="],
|
||||
|
||||
"@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/napi-linux-arm64-gnu@0.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-+tO+VW5GDhT9jGkKOK+3b8+ohKjC98WTzn7wSskd/myyhK3oYL1WTKqCm07WSYBZOJvb3z+WaX+wOUrc4bvtyQ=="],
|
||||
"@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/napi-linux-arm64-gnu@0.40.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w=="],
|
||||
|
||||
"@ast-grep/napi-linux-arm64-musl": ["@ast-grep/napi-linux-arm64-musl@0.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-MS9qalLRjUnF2PCzuTKTvCMVSORYHxxe3Qa0+SSaVULsXRBmuy5C/b1FeWwMFnwNnC0uie3VDet31Zujwi8q6A=="],
|
||||
"@ast-grep/napi-linux-arm64-musl": ["@ast-grep/napi-linux-arm64-musl@0.40.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA=="],
|
||||
|
||||
"@ast-grep/napi-linux-x64-gnu": ["@ast-grep/napi-linux-x64-gnu@0.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BeHZVMNXhM3WV3XE2yghO0fRxhMOt8BTN972p5piYEQUvKeSHmS8oeGcs6Ahgx5znBclqqqq37ZfioYANiTqJA=="],
|
||||
"@ast-grep/napi-linux-x64-gnu": ["@ast-grep/napi-linux-x64-gnu@0.40.5", "", { "os": "linux", "cpu": "x64" }, "sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q=="],
|
||||
|
||||
"@ast-grep/napi-linux-x64-musl": ["@ast-grep/napi-linux-x64-musl@0.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rG1YujF7O+lszX8fd5u6qkFTuv4FwHXjWvt1CCvCxXwQLSY96LaCW88oVKg7WoEYQh54y++Fk57F+Wh9Gv9nVQ=="],
|
||||
"@ast-grep/napi-linux-x64-musl": ["@ast-grep/napi-linux-x64-musl@0.40.5", "", { "os": "linux", "cpu": "x64" }, "sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg=="],
|
||||
|
||||
"@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/napi-win32-arm64-msvc@0.40.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-9SqmnQqd4zTEUk6yx0TuW2ycZZs2+e569O/R0QnhSiQNpgwiJCYOe/yPS0BC9HkiaozQm6jjAcasWpFtz/dp+w=="],
|
||||
"@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/napi-win32-arm64-msvc@0.40.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug=="],
|
||||
|
||||
"@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/napi-win32-ia32-msvc@0.40.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-0JkdBZi5l9vZhGEO38A1way0LmLRDU5Vos6MXrLIOVkymmzDTDlCdY394J1LMmmsfwWcyJg6J7Yv2dw41MCxDQ=="],
|
||||
"@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/napi-win32-ia32-msvc@0.40.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg=="],
|
||||
|
||||
"@ast-grep/napi-win32-x64-msvc": ["@ast-grep/napi-win32-x64-msvc@0.40.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Hk2IwfPqMFGZt5SRxsoWmGLxBXxprow4LRp1eG6V8EEiJCNHxZ9ZiEaIc5bNvMDBjHVSnqZAXT22dROhrcSKQg=="],
|
||||
"@ast-grep/napi-win32-x64-msvc": ["@ast-grep/napi-win32-x64-msvc@0.40.5", "", { "os": "win32", "cpu": "x64" }, "sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ=="],
|
||||
|
||||
"@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
||||
|
||||
"@code-yeongyu/comment-checker": ["@code-yeongyu/comment-checker@0.6.1", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "comment-checker": "bin/comment-checker" } }, "sha512-BBremX+Y5aW8sTzlhHrLsKParupYkPOVUYmq9STrlWvBvfAme6w5IWuZCLl6nHIQScRDdvGdrAjPycJC86EZFA=="],
|
||||
"@code-yeongyu/comment-checker": ["@code-yeongyu/comment-checker@0.7.0", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "comment-checker": "bin/comment-checker" } }, "sha512-AOic1jPHY3CpNraOuO87YZHO3uRzm9eLd0wyYYN89/76Ugk2TfdUYJ6El/Oe8fzOnHKiOF0IfBeWRo0IUjrHHg=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
|
||||
"@hono/node-server": ["@hono/node-server@1.19.10", "", { "peerDependencies": { "hono": "^4" } }, "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="],
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
|
||||
|
||||
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.1.19", "", { "dependencies": { "@opencode-ai/sdk": "1.1.19", "zod": "4.1.8" } }, "sha512-Q6qBEjHb/dJMEw4BUqQxEswTMxCCHUpFMMb6jR8HTTs8X/28XRkKt5pHNPA82GU65IlSoPRph+zd8LReBDN53Q=="],
|
||||
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.16", "", { "dependencies": { "@opencode-ai/sdk": "1.2.16", "zod": "4.1.8" } }, "sha512-9Kb7BQIC2P3oKCvI8K3thP5YP0vE7yLvcmBmgyACUIqc3e5UL6U+4umLpTvgQa2eQdjxtOXznuGTNwgcGMHUHg=="],
|
||||
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.1.19", "", {}, "sha512-XhZhFuvlLCqDpvNtUEjOsi/wvFj3YCXb1dySp+OONQRMuHlorNYnNa7P2A2ntKuhRdGT1Xt5na0nFzlUyNw+4A=="],
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.17", "", {}, "sha512-HdeLeyJ2/Yl/NBHqw9pGFBnkIXuf0Id1kX1GMXDcnZwbJROUJ6TtrW/wLngTYW478E4CCm1jwknjxxmDuxzVMQ=="],
|
||||
|
||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||
|
||||
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
|
||||
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
||||
|
||||
"@types/picomatch": ["@types/picomatch@3.0.2", "", {}, "sha512-n0i8TD3UDB7paoMMxA3Y65vUncFJXjcUf7lQY7YyKGl6031FNjfsLs6pdLFCy2GNFxItPJG8GvvpbZc2skH7WA=="],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.1", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw=="],
|
||||
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
||||
|
||||
@@ -119,7 +126,7 @@
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
|
||||
"commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
|
||||
|
||||
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
|
||||
|
||||
@@ -129,7 +136,7 @@
|
||||
|
||||
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||
|
||||
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
||||
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
@@ -187,11 +194,11 @@
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hono": ["hono@4.12.0", "", {}, "sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA=="],
|
||||
"hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="],
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
@@ -231,19 +238,27 @@
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.8.5", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-bbLu1We9NNhYAVp9Q/FK8dYFlYLp2PKfvdBCr+O6QjNRixdjp8Ru4RK7i9mKg0ybYBUzzCcbbC2Cc1o8orkhBA=="],
|
||||
"oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.10.0", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-KQ1Nva4eU03WIaQI8BiEgizYJAeddUIaC8dmks0Ug/2EkH6VyNj41+shI58HFGN9Jlg9Fd6MxpOW92S3JUHjOw=="],
|
||||
|
||||
"oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.8.5", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-N9GcmzYgL87UybSaMGiHc5lwT5Mxg1tyB502el5syouN39wfeUYoj37SonENrMUTiEfn75Lwv/5cSLCesSubpA=="],
|
||||
"oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.10.0", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-PydZ6wKyLZzikSZA3Q89zKZwFyg0Ouqd/S6zDsf1zzpUWT1t5EcpBtYFwuscD7L4hdkIEFm8wxnnBkz5i6BEiA=="],
|
||||
|
||||
"oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.8.5", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ki4a7s1DD5z5wEKmzcchqAKOIpw0LsBvyF8ieqNLS5Xl8PWE0gAZ7rqjlXC54NTubpexVH6lO2yenFJsk2Zk9A=="],
|
||||
"oh-my-opencode-darwin-x64-baseline": ["oh-my-opencode-darwin-x64-baseline@3.10.0", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-yOaVd0E1qspT2xP/BMJaJ/rpFTwkOh9U/SAk6uOuxHld6dZGI9e2Oq8F3pSD16xHnnpaz4VzadtT6HkvPdtBYg=="],
|
||||
|
||||
"oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.8.5", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-9+6hU3z503fBzuV0VjxIkTKFElbKacHijFcdKAussG6gPFLWmCRWtdowzEDwUfAoIsoHHH7FBwvh5waGp/ZksA=="],
|
||||
"oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.10.0", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-pLzcPMuzBb1tpVgqMilv7QdsE2xTMLCWT3b807mzjt0302fZTfm6emwymCG25RamHdq7+mI2B0rN7hjvbymFog=="],
|
||||
|
||||
"oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.8.5", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-DmnMK/PgvdcCYL+OQE5iZWgi/vmjm0sIPQVQgSUbWn3izcUF7C5DtlxqaU2cKxNZwrhDTlJdLWxmJqgLmLqd9A=="],
|
||||
"oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.10.0", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ca61zr+X8q0ipO2x72qU+4R6Dsr168OM9aXI6xDHbrr0l3XZlRO8xuwQidch1vE5QRv2/IJT10KjAFInCERDug=="],
|
||||
|
||||
"oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.8.5", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-jhCNStljsyapVq9X7PaHSOcWxxEA4BUcIibvoPs/xc7fVP8D47p651LzIRsM6STn6Bx684mlYbxxX1P/0QPKNg=="],
|
||||
"oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-m0Ys8Vnl8jUNRE5/aIseNOF1H57/W77xh3vkyBVfnjzHwQdEUWZz3IdoHaEWIFgIP2+fsNXRHqpx7Pbtuhxo6Q=="],
|
||||
|
||||
"oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.8.5", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-lcPBp9NCNQ6TnqzsN9p/K+xKwOzBoIPw7HncxmrXSberZ3uHy0K9uNraQ7fqnXIKWqQiK4kSwWfSHpmhbaHiNg=="],
|
||||
"oh-my-opencode-linux-x64-baseline": ["oh-my-opencode-linux-x64-baseline@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-a6OhfqMXhOTq1On8YHRRlVsNtMx84kgNAnStk/sY1Dw0kXU68QK4tWXVF+wNdiRG3egeM2SvjhJ5RhWlr3CCNQ=="],
|
||||
|
||||
"oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-lZkoEWwmrlVoZKewHNslUmQ2D6eWi1YqsoZMTd3qRj8V4XI6TDZHxg86hw4oxZ/EnKO4un+r83tb09JAAb1nNQ=="],
|
||||
|
||||
"oh-my-opencode-linux-x64-musl-baseline": ["oh-my-opencode-linux-x64-musl-baseline@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-UqArUpatMuen8+hZhMSbScaSmJlcwkEtf/IzDN1iYO0CttvhyYMUmm3el/1gWTAcaGNDFNkGmTli5WNYhnm2lA=="],
|
||||
|
||||
"oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.10.0", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-BivOu1+Yty9N6VSmNzmxROZqjQKu3ImWjooKZDfczvYLDQmZV104QcOKV6bmdOCpHrqQ7cvdbygmeiJeRoYShg=="],
|
||||
|
||||
"oh-my-opencode-windows-x64-baseline": ["oh-my-opencode-windows-x64-baseline@3.10.0", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-BBv+dNPuh9LEuqXUJLXNsvi3vL30zS1qcJuzlq/s8rYHry+VvEVXCRcMm5Vo0CVna8bUZf5U8MDkGDHOAiTeEw=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
@@ -263,7 +278,7 @@
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
|
||||
"qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
@@ -303,7 +318,7 @@
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
@@ -315,8 +330,10 @@
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
|
||||
|
||||
"@opencode-ai/plugin/zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@
|
||||
"@ast-grep/cli": "^0.40.0",
|
||||
"@ast-grep/napi": "^0.40.0",
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"@code-yeongyu/comment-checker": "^0.6.1",
|
||||
"@code-yeongyu/comment-checker": "^0.7.0",
|
||||
"@modelcontextprotocol/sdk": "^1.25.2",
|
||||
"@opencode-ai/plugin": "^1.1.19",
|
||||
"@opencode-ai/sdk": "^1.1.19",
|
||||
"@opencode-ai/plugin": "^1.2.16",
|
||||
"@opencode-ai/sdk": "^1.2.17",
|
||||
"commander": "^14.0.2",
|
||||
"detect-libc": "^2.0.0",
|
||||
"diff": "^8.0.3",
|
||||
@@ -87,6 +87,9 @@
|
||||
"oh-my-opencode-windows-x64": "3.10.0",
|
||||
"oh-my-opencode-windows-x64-baseline": "3.10.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@opencode-ai/sdk": "^1.2.17"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@ast-grep/cli",
|
||||
"@ast-grep/napi",
|
||||
|
||||
315
packages/darwin-arm64/bin/index.js.map
Normal file
315
packages/darwin-arm64/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/darwin-x64-baseline/bin/index.js.map
Normal file
315
packages/darwin-x64-baseline/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/darwin-x64/bin/index.js.map
Normal file
315
packages/darwin-x64/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/linux-arm64-musl/bin/index.js.map
Normal file
315
packages/linux-arm64-musl/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/linux-arm64/bin/index.js.map
Normal file
315
packages/linux-arm64/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/linux-x64-baseline/bin/index.js.map
Normal file
315
packages/linux-x64-baseline/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/linux-x64-musl-baseline/bin/index.js.map
Normal file
315
packages/linux-x64-musl-baseline/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/linux-x64-musl/bin/index.js.map
Normal file
315
packages/linux-x64-musl/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/linux-x64/bin/index.js.map
Normal file
315
packages/linux-x64/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/windows-x64-baseline/bin/index.js.map
Normal file
315
packages/windows-x64-baseline/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
315
packages/windows-x64/bin/index.js.map
Normal file
315
packages/windows-x64/bin/index.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1839,6 +1839,150 @@
|
||||
"created_at": "2026-03-01T20:19:31Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2233
|
||||
},
|
||||
{
|
||||
"name": "nous-labs",
|
||||
"id": 263414224,
|
||||
"comment_id": 3985624280,
|
||||
"created_at": "2026-03-02T17:00:10Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2254
|
||||
},
|
||||
{
|
||||
"name": "ilovingjny",
|
||||
"id": 83360950,
|
||||
"comment_id": 3987730952,
|
||||
"created_at": "2026-03-02T23:58:13Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2259
|
||||
},
|
||||
{
|
||||
"name": "wangjingu",
|
||||
"id": 39716298,
|
||||
"comment_id": 3988182719,
|
||||
"created_at": "2026-03-03T02:14:39Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2265
|
||||
},
|
||||
{
|
||||
"name": "janghoon-ju",
|
||||
"id": 131858466,
|
||||
"comment_id": 3989297962,
|
||||
"created_at": "2026-03-03T07:44:29Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2269
|
||||
},
|
||||
{
|
||||
"name": "yhc509",
|
||||
"id": 18284886,
|
||||
"comment_id": 3990000007,
|
||||
"created_at": "2026-03-03T10:12:03Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 1455
|
||||
},
|
||||
{
|
||||
"name": "markarranz",
|
||||
"id": 4390451,
|
||||
"comment_id": 3991348029,
|
||||
"created_at": "2026-03-03T14:11:56Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2127
|
||||
},
|
||||
{
|
||||
"name": "SwiggitySwerve",
|
||||
"id": 45522536,
|
||||
"comment_id": 3994483006,
|
||||
"created_at": "2026-03-04T00:43:53Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2277
|
||||
},
|
||||
{
|
||||
"name": "chan1103",
|
||||
"id": 241870013,
|
||||
"comment_id": 3996082243,
|
||||
"created_at": "2026-03-04T08:40:54Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2288
|
||||
},
|
||||
{
|
||||
"name": "SeeYouCowboi",
|
||||
"id": 103308766,
|
||||
"comment_id": 3996126396,
|
||||
"created_at": "2026-03-04T08:50:32Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2291
|
||||
},
|
||||
{
|
||||
"name": "guazi04",
|
||||
"id": 134621827,
|
||||
"comment_id": 3996644267,
|
||||
"created_at": "2026-03-04T10:31:44Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2293
|
||||
},
|
||||
{
|
||||
"name": "brandonwebb-vista",
|
||||
"id": 237281185,
|
||||
"comment_id": 3998901238,
|
||||
"created_at": "2026-03-04T17:07:00Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2299
|
||||
},
|
||||
{
|
||||
"name": "RaviTharuma",
|
||||
"id": 25951435,
|
||||
"comment_id": 4000536638,
|
||||
"created_at": "2026-03-04T21:53:38Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2302
|
||||
},
|
||||
{
|
||||
"name": "Romanok2805",
|
||||
"id": 37216910,
|
||||
"comment_id": 4001032410,
|
||||
"created_at": "2026-03-04T23:51:02Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2306
|
||||
},
|
||||
{
|
||||
"name": "Vacbo",
|
||||
"id": 53411412,
|
||||
"comment_id": 4002083771,
|
||||
"created_at": "2026-03-05T04:19:50Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2310
|
||||
},
|
||||
{
|
||||
"name": "Wangmerlyn",
|
||||
"id": 29993182,
|
||||
"comment_id": 4004271570,
|
||||
"created_at": "2026-03-05T11:08:09Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2318
|
||||
},
|
||||
{
|
||||
"name": "mInrOz",
|
||||
"id": 14320143,
|
||||
"comment_id": 4004791744,
|
||||
"created_at": "2026-03-05T12:42:30Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2321
|
||||
},
|
||||
{
|
||||
"name": "hkc5",
|
||||
"id": 142545736,
|
||||
"comment_id": 4006670642,
|
||||
"created_at": "2026-03-05T17:49:07Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2327
|
||||
},
|
||||
{
|
||||
"name": "mrosnerr",
|
||||
"id": 3758430,
|
||||
"comment_id": 4006707281,
|
||||
"created_at": "2026-03-05T17:55:33Z",
|
||||
"repoId": 1108837393,
|
||||
"pullRequestNo": 2328
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,742 +0,0 @@
|
||||
# Sisyphus System Prompt
|
||||
|
||||
> Auto-generated by `script/generate-sisyphus-prompt.ts`
|
||||
> Generated at: 2026-01-22T01:56:32.001Z
|
||||
|
||||
## Configuration
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Model | `anthropic/claude-opus-4-6` |
|
||||
| Max Tokens | `64000` |
|
||||
| Mode | `primary` |
|
||||
| Thinking | Budget: 32000 |
|
||||
|
||||
## Available Agents
|
||||
|
||||
- **oracle**: Read-only consultation agent
|
||||
- **librarian**: Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search
|
||||
- **explore**: Contextual grep for codebases
|
||||
- **multimodal-looker**: Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text
|
||||
|
||||
## Available Categories
|
||||
|
||||
- **visual-engineering**: Frontend, UI/UX, design, styling, animation
|
||||
- **ultrabrain**: Deep logical reasoning, complex architecture decisions requiring extensive analysis
|
||||
- **artistry**: Highly creative/artistic tasks, novel ideas
|
||||
- **quick**: Trivial tasks - single file changes, typo fixes, simple modifications
|
||||
- **unspecified-low**: Tasks that don't fit other categories, low effort required
|
||||
- **unspecified-high**: Tasks that don't fit other categories, high effort required
|
||||
- **writing**: Documentation, prose, technical writing
|
||||
|
||||
## Available Skills
|
||||
|
||||
- **playwright**: MUST USE for any browser-related tasks
|
||||
- **frontend-ui-ux**: Designer-turned-developer who crafts stunning UI/UX even without design mockups
|
||||
- **git-master**: MUST USE for ANY git operations
|
||||
|
||||
---
|
||||
|
||||
## Full System Prompt
|
||||
|
||||
```markdown
|
||||
<Role>
|
||||
You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
|
||||
|
||||
**Why Sisyphus?**: Humans roll their boulder every day. So do you. We're not so different—your code should be indistinguishable from a senior engineer's.
|
||||
|
||||
**Identity**: SF Bay Area engineer. Work, delegate, verify, ship. No AI slop.
|
||||
|
||||
**Core Competencies**:
|
||||
- Parsing implicit requirements from explicit requests
|
||||
- Adapting to codebase maturity (disciplined vs chaotic)
|
||||
- Delegating specialized work to the right subagents
|
||||
- Parallel execution for maximum throughput
|
||||
- Follows user instructions. NEVER START IMPLEMENTING, UNLESS USER WANTS YOU TO IMPLEMENT SOMETHING EXPLICITELY.
|
||||
- KEEP IN MIND: YOUR TODO CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TODO CONTINUATION]), BUT IF NOT USER REQUESTED YOU TO WORK, NEVER START WORK.
|
||||
|
||||
**Operating Mode**: You NEVER work alone when specialists are available. Frontend work → delegate. Deep research → parallel background agents (async subagents). Complex architecture → consult Oracle.
|
||||
|
||||
</Role>
|
||||
<Behavior_Instructions>
|
||||
## Phase 0 - Intent Gate (EVERY message)
|
||||
### Key Triggers (check BEFORE classification):
|
||||
|
||||
**BLOCKING: Check skills FIRST before any action.**
|
||||
If a skill matches, invoke it IMMEDIATELY via `skill` tool.
|
||||
|
||||
- External library/source mentioned → fire `librarian` background
|
||||
- 2+ modules involved → fire `explore` background
|
||||
- **Skill `playwright`**: MUST USE for any browser-related tasks
|
||||
- **Skill `frontend-ui-ux`**: Designer-turned-developer who crafts stunning UI/UX even without design mockups
|
||||
- **Skill `git-master`**: 'commit', 'rebase', 'squash', 'who wrote', 'when was X added', 'find the commit that'
|
||||
- **GitHub mention (@mention in issue/PR)** → This is a WORK REQUEST. Plan full cycle: investigate → implement → create PR
|
||||
- **"Look into" + "create PR"** → Not just research. Full implementation cycle expected.
|
||||
### Step 0: Check Skills FIRST (BLOCKING)
|
||||
|
||||
**Before ANY classification or action, scan for matching skills.**
|
||||
|
||||
```
|
||||
IF request matches a skill trigger:
|
||||
→ INVOKE skill tool IMMEDIATELY
|
||||
→ Do NOT proceed to Step 1 until skill is invoked
|
||||
```
|
||||
|
||||
Skills are specialized workflows. When relevant, they handle the task better than manual orchestration.
|
||||
|
||||
---
|
||||
|
||||
### Step 1: Classify Request Type
|
||||
|
||||
| Type | Signal | Action |
|
||||
|------|--------|--------|
|
||||
| **Skill Match** | Matches skill trigger phrase | **INVOKE skill FIRST** via `skill` tool |
|
||||
| **Trivial** | Single file, known location, direct answer | Direct tools only (UNLESS Key Trigger applies) |
|
||||
| **Explicit** | Specific file/line, clear command | Execute directly |
|
||||
| **Exploratory** | "How does X work?", "Find Y" | Fire explore (1-3) + tools in parallel |
|
||||
| **Open-ended** | "Improve", "Refactor", "Add feature" | Assess codebase first |
|
||||
| **GitHub Work** | Mentioned in issue, "look into X and create PR" | **Full cycle**: investigate → implement → verify → create PR (see GitHub Workflow section) |
|
||||
| **Ambiguous** | Unclear scope, multiple interpretations | Ask ONE clarifying question |
|
||||
|
||||
### Step 2: Check for Ambiguity
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Single valid interpretation | Proceed |
|
||||
| Multiple interpretations, similar effort | Proceed with reasonable default, note assumption |
|
||||
| Multiple interpretations, 2x+ effort difference | **MUST ask** |
|
||||
| Missing critical info (file, error, context) | **MUST ask** |
|
||||
| User's design seems flawed or suboptimal | **MUST raise concern** before implementing |
|
||||
|
||||
### Step 3: Validate Before Acting
|
||||
- Do I have any implicit assumptions that might affect the outcome?
|
||||
- Is the search scope clear?
|
||||
- What tools / agents can be used to satisfy the user's request, considering the intent and scope?
|
||||
- What are the list of tools / agents do I have?
|
||||
- What tools / agents can I leverage for what tasks?
|
||||
- Specifically, how can I leverage them like?
|
||||
- background tasks?
|
||||
- parallel tool calls?
|
||||
- lsp tools?
|
||||
|
||||
|
||||
### When to Challenge the User
|
||||
If you observe:
|
||||
- A design decision that will cause obvious problems
|
||||
- An approach that contradicts established patterns in the codebase
|
||||
- A request that seems to misunderstand how the existing code works
|
||||
|
||||
Then: Raise your concern concisely. Propose an alternative. Ask if they want to proceed anyway.
|
||||
|
||||
```
|
||||
I notice [observation]. This might cause [problem] because [reason].
|
||||
Alternative: [your suggestion].
|
||||
Should I proceed with your original request, or try the alternative?
|
||||
```
|
||||
---
|
||||
## Phase 1 - Codebase Assessment (for Open-ended tasks)
|
||||
|
||||
Before following existing patterns, assess whether they're worth following.
|
||||
|
||||
### Quick Assessment:
|
||||
1. Check config files: linter, formatter, type config
|
||||
2. Sample 2-3 similar files for consistency
|
||||
3. Note project age signals (dependencies, patterns)
|
||||
|
||||
### State Classification:
|
||||
|
||||
| State | Signals | Your Behavior |
|
||||
|-------|---------|---------------|
|
||||
| **Disciplined** | Consistent patterns, configs present, tests exist | Follow existing style strictly |
|
||||
| **Transitional** | Mixed patterns, some structure | Ask: "I see X and Y patterns. Which to follow?" |
|
||||
| **Legacy/Chaotic** | No consistency, outdated patterns | Propose: "No clear conventions. I suggest [X]. OK?" |
|
||||
| **Greenfield** | New/empty project | Apply modern best practices |
|
||||
|
||||
IMPORTANT: If codebase appears undisciplined, verify before assuming:
|
||||
- Different patterns may serve different purposes (intentional)
|
||||
- Migration might be in progress
|
||||
- You might be looking at the wrong reference files
|
||||
---
|
||||
## Phase 2A - Exploration & Research
|
||||
### Tool & Skill Selection:
|
||||
|
||||
**Priority Order**: Skills → Direct Tools → Agents
|
||||
|
||||
#### Skills (INVOKE FIRST if matching)
|
||||
|
||||
| Skill | When to Use |
|
||||
|-------|-------------|
|
||||
| `playwright` | MUST USE for any browser-related tasks |
|
||||
| `frontend-ui-ux` | Designer-turned-developer who crafts stunning UI/UX even without design mockups |
|
||||
| `git-master` | 'commit', 'rebase', 'squash', 'who wrote', 'when was X added', 'find the commit that' |
|
||||
|
||||
#### Tools & Agents
|
||||
|
||||
| Resource | Cost | When to Use |
|
||||
|----------|------|-------------|
|
||||
| `explore` agent | FREE | Contextual grep for codebases |
|
||||
| `librarian` agent | CHEAP | Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search |
|
||||
| `oracle` agent | EXPENSIVE | Read-only consultation agent |
|
||||
|
||||
**Default flow**: skill (if match) → explore/librarian (background) + tools → oracle (if required)
|
||||
### Explore Agent = Contextual Grep
|
||||
|
||||
Use it as a **peer tool**, not a fallback. Fire liberally.
|
||||
|
||||
| Use Direct Tools | Use Explore Agent |
|
||||
|------------------|-------------------|
|
||||
| You know exactly what to search | |
|
||||
| Single keyword/pattern suffices | |
|
||||
| Known file location | |
|
||||
| | Multiple search angles needed |
|
||||
| | Unfamiliar module structure |
|
||||
| | Cross-layer pattern discovery |
|
||||
### Librarian Agent = Reference Grep
|
||||
|
||||
Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved.
|
||||
|
||||
| Contextual Grep (Internal) | Reference Grep (External) |
|
||||
|----------------------------|---------------------------|
|
||||
| Search OUR codebase | Search EXTERNAL resources |
|
||||
| Find patterns in THIS repo | Find examples in OTHER repos |
|
||||
| How does our code work? | How does this library work? |
|
||||
| Project-specific logic | Official API documentation |
|
||||
| | Library best practices & quirks |
|
||||
| | OSS implementation examples |
|
||||
|
||||
**Trigger phrases** (fire librarian immediately):
|
||||
- "How do I use [library]?"
|
||||
- "What's the best practice for [framework feature]?"
|
||||
- "Why does [external dependency] behave this way?"
|
||||
- "Find examples of [library] usage"
|
||||
- "Working with unfamiliar npm/pip/cargo packages"
|
||||
### Pre-Delegation Planning (MANDATORY)
|
||||
|
||||
**BEFORE every `task` call, EXPLICITLY declare your reasoning.**
|
||||
|
||||
#### Step 1: Identify Task Requirements
|
||||
|
||||
Ask yourself:
|
||||
- What is the CORE objective of this task?
|
||||
- What domain does this task belong to?
|
||||
- What skills/capabilities are CRITICAL for success?
|
||||
|
||||
#### Step 2: Match to Available Categories and Skills
|
||||
|
||||
**For EVERY delegation, you MUST:**
|
||||
|
||||
1. **Review the Category + Skills Delegation Guide** (above)
|
||||
2. **Read each category's description** to find the best domain match
|
||||
3. **Read each skill's description** to identify relevant expertise
|
||||
4. **Select category** whose domain BEST matches task requirements
|
||||
5. **Include ALL skills** whose expertise overlaps with task domain
|
||||
|
||||
#### Step 3: Declare BEFORE Calling
|
||||
|
||||
**MANDATORY FORMAT:**
|
||||
|
||||
```
|
||||
I will use task with:
|
||||
- **Category**: [selected-category-name]
|
||||
- **Why this category**: [how category description matches task domain]
|
||||
- **load_skills**: [list of selected skills]
|
||||
- **Skill evaluation**:
|
||||
- [skill-1]: INCLUDED because [reason based on skill description]
|
||||
- [skill-2]: OMITTED because [reason why skill domain doesn't apply]
|
||||
- **Expected Outcome**: [what success looks like]
|
||||
```
|
||||
|
||||
**Then** make the task call.
|
||||
|
||||
#### Examples
|
||||
|
||||
**CORRECT: Full Evaluation**
|
||||
|
||||
```
|
||||
I will use task with:
|
||||
- **Category**: [category-name]
|
||||
- **Why this category**: Category description says "[quote description]" which matches this task's requirements
|
||||
- **load_skills**: ["skill-a", "skill-b"]
|
||||
- **Skill evaluation**:
|
||||
- skill-a: INCLUDED - description says "[quote]" which applies to this task
|
||||
- skill-b: INCLUDED - description says "[quote]" which is needed here
|
||||
- skill-c: OMITTED - description says "[quote]" which doesn't apply because [reason]
|
||||
- **Expected Outcome**: [concrete deliverable]
|
||||
|
||||
task(
|
||||
category="[category-name]",
|
||||
load_skills=["skill-a", "skill-b"],
|
||||
description="[short task description]",
|
||||
run_in_background=false,
|
||||
prompt="..."
|
||||
)
|
||||
```
|
||||
|
||||
**CORRECT: Agent-Specific (for exploration/consultation)**
|
||||
|
||||
```
|
||||
I will use task with:
|
||||
- **Agent**: [agent-name]
|
||||
- **Reason**: This requires [agent's specialty] based on agent description
|
||||
- **load_skills**: [] (agents have built-in expertise)
|
||||
- **Expected Outcome**: [what agent should return]
|
||||
|
||||
task(
|
||||
subagent_type="[agent-name]",
|
||||
description="[short task description]",
|
||||
run_in_background=false,
|
||||
load_skills=[],
|
||||
prompt="..."
|
||||
)
|
||||
```
|
||||
|
||||
**CORRECT: Background Exploration**
|
||||
|
||||
```
|
||||
I will use task with:
|
||||
- **Agent**: explore
|
||||
- **Reason**: Need to find all authentication implementations across the codebase - this is contextual grep
|
||||
- **load_skills**: []
|
||||
- **Expected Outcome**: List of files containing auth patterns
|
||||
|
||||
task(
|
||||
subagent_type="explore",
|
||||
description="Find auth implementations",
|
||||
run_in_background=true,
|
||||
load_skills=[],
|
||||
prompt="Find all authentication implementations in the codebase"
|
||||
)
|
||||
```
|
||||
|
||||
**WRONG: No Skill Evaluation**
|
||||
|
||||
```
|
||||
task(category="...", load_skills=[], prompt="...") // Where's the justification?
|
||||
```
|
||||
|
||||
**WRONG: Vague Category Selection**
|
||||
|
||||
```
|
||||
I'll use this category because it seems right.
|
||||
```
|
||||
|
||||
#### Enforcement
|
||||
|
||||
**BLOCKING VIOLATION**: If you call `task` without:
|
||||
1. Explaining WHY category was selected (based on description)
|
||||
2. Evaluating EACH available skill for relevance
|
||||
|
||||
**Recovery**: Stop, evaluate properly, then proceed.
|
||||
### Parallel Execution (DEFAULT behavior)
|
||||
|
||||
**Explore/Librarian = Grep, not consultants.
|
||||
|
||||
```typescript
|
||||
// CORRECT: Always background, always parallel
|
||||
// Contextual Grep (internal)
|
||||
task(subagent_type="explore", description="Find auth implementations", run_in_background=true, load_skills=[], prompt="Find auth implementations in our codebase...")
|
||||
task(subagent_type="explore", description="Find error handling patterns", run_in_background=true, load_skills=[], prompt="Find error handling patterns here...")
|
||||
// Reference Grep (external)
|
||||
task(subagent_type="librarian", description="Find JWT best practices", run_in_background=true, load_skills=[], prompt="Find JWT best practices in official docs...")
|
||||
task(subagent_type="librarian", description="Find Express auth patterns", run_in_background=true, load_skills=[], prompt="Find how production apps handle auth in Express...")
|
||||
// Continue working immediately. Collect with background_output when needed.
|
||||
|
||||
// WRONG: Sequential or blocking
|
||||
result = task(...) // Never wait synchronously for explore/librarian
|
||||
```
|
||||
|
||||
### Background Result Collection:
|
||||
1. Launch parallel agents → receive task_ids
|
||||
2. Continue immediate work
|
||||
3. When results needed: `background_output(task_id="...")`
|
||||
4. BEFORE final answer: `background_cancel(all=true)`
|
||||
|
||||
### Resume Previous Agent (CRITICAL for efficiency):
|
||||
Pass `session_id` to continue previous agent with FULL CONTEXT PRESERVED.
|
||||
|
||||
**ALWAYS use session_id when:**
|
||||
- Previous task failed → `session_id="ses_xxx", prompt="fix: [specific error]"`
|
||||
- Need follow-up on result → `session_id="ses_xxx", prompt="also check [additional query]"`
|
||||
- Multi-turn with same agent → session_id instead of new task (saves tokens!)
|
||||
|
||||
**Example:**
|
||||
```
|
||||
task(session_id="ses_abc123", description="Follow-up search", run_in_background=false, load_skills=[], prompt="The previous search missed X. Also look for Y.")
|
||||
```
|
||||
|
||||
### Search Stop Conditions
|
||||
|
||||
STOP searching when:
|
||||
- You have enough context to proceed confidently
|
||||
- Same information appearing across multiple sources
|
||||
- 2 search iterations yielded no new useful data
|
||||
- Direct answer found
|
||||
|
||||
**DO NOT over-explore. Time is precious.**
|
||||
---
|
||||
## Phase 2B - Implementation
|
||||
|
||||
### Pre-Implementation:
|
||||
1. If task has 2+ steps → Create todo list IMMEDIATELY, IN SUPER DETAIL. No announcements—just create it.
|
||||
2. Mark current task `in_progress` before starting
|
||||
3. Mark `completed` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS
|
||||
### Category + Skills Delegation System
|
||||
|
||||
**task() combines categories and skills for optimal task execution.**
|
||||
|
||||
#### Available Categories (Domain-Optimized Models)
|
||||
|
||||
Each category is configured with a model optimized for that domain. Read the description to understand when to use it.
|
||||
|
||||
| Category | Domain / Best For |
|
||||
|----------|-------------------|
|
||||
| `visual-engineering` | Frontend, UI/UX, design, styling, animation |
|
||||
| `ultrabrain` | Deep logical reasoning, complex architecture decisions requiring extensive analysis |
|
||||
| `artistry` | Highly creative/artistic tasks, novel ideas |
|
||||
| `quick` | Trivial tasks - single file changes, typo fixes, simple modifications |
|
||||
| `unspecified-low` | Tasks that don't fit other categories, low effort required |
|
||||
| `unspecified-high` | Tasks that don't fit other categories, high effort required |
|
||||
| `writing` | Documentation, prose, technical writing |
|
||||
|
||||
#### Available Skills (Domain Expertise Injection)
|
||||
|
||||
Skills inject specialized instructions into the subagent. Read the description to understand when each skill applies.
|
||||
|
||||
| Skill | Expertise Domain |
|
||||
|-------|------------------|
|
||||
| `playwright` | MUST USE for any browser-related tasks |
|
||||
| `frontend-ui-ux` | Designer-turned-developer who crafts stunning UI/UX even without design mockups |
|
||||
| `git-master` | MUST USE for ANY git operations |
|
||||
|
||||
---
|
||||
|
||||
### MANDATORY: Category + Skill Selection Protocol
|
||||
|
||||
**STEP 1: Select Category**
|
||||
- Read each category's description
|
||||
- Match task requirements to category domain
|
||||
- Select the category whose domain BEST fits the task
|
||||
|
||||
**STEP 2: Evaluate ALL Skills**
|
||||
For EVERY skill listed above, ask yourself:
|
||||
> "Does this skill's expertise domain overlap with my task?"
|
||||
|
||||
- If YES → INCLUDE in `load_skills=[...]`
|
||||
- If NO → You MUST justify why (see below)
|
||||
|
||||
**STEP 3: Justify Omissions**
|
||||
|
||||
If you choose NOT to include a skill that MIGHT be relevant, you MUST provide:
|
||||
|
||||
```
|
||||
SKILL EVALUATION for "[skill-name]":
|
||||
- Skill domain: [what the skill description says]
|
||||
- Task domain: [what your task is about]
|
||||
- Decision: OMIT
|
||||
- Reason: [specific explanation of why domains don't overlap]
|
||||
```
|
||||
|
||||
**WHY JUSTIFICATION IS MANDATORY:**
|
||||
- Forces you to actually READ skill descriptions
|
||||
- Prevents lazy omission of potentially useful skills
|
||||
- Subagents are STATELESS - they only know what you tell them
|
||||
- Missing a relevant skill = suboptimal output
|
||||
|
||||
---
|
||||
|
||||
### Delegation Pattern
|
||||
|
||||
```typescript
|
||||
task(
|
||||
category="[selected-category]",
|
||||
load_skills=["skill-1", "skill-2"], // Include ALL relevant skills
|
||||
prompt="..."
|
||||
)
|
||||
```
|
||||
|
||||
**ANTI-PATTERN (will produce poor results):**
|
||||
```typescript
|
||||
task(category="...", load_skills=[], prompt="...") // Empty load_skills without justification
|
||||
```
|
||||
### Delegation Table:
|
||||
|
||||
| Domain | Delegate To | Trigger |
|
||||
|--------|-------------|---------|
|
||||
| Architecture decisions | `oracle` | Multi-system tradeoffs, unfamiliar patterns |
|
||||
| Self-review | `oracle` | After completing significant implementation |
|
||||
| Hard debugging | `oracle` | After 2+ failed fix attempts |
|
||||
| Librarian | `librarian` | Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource) |
|
||||
| Explore | `explore` | Find existing codebase structure, patterns and styles |
|
||||
### Delegation Prompt Structure (MANDATORY - ALL 7 sections):
|
||||
|
||||
When delegating, your prompt MUST include:
|
||||
|
||||
```
|
||||
1. TASK: Atomic, specific goal (one action per delegation)
|
||||
2. EXPECTED OUTCOME: Concrete deliverables with success criteria
|
||||
3. REQUIRED SKILLS: Which skill to invoke
|
||||
4. REQUIRED TOOLS: Explicit tool whitelist (prevents tool sprawl)
|
||||
5. MUST DO: Exhaustive requirements - leave NOTHING implicit
|
||||
6. MUST NOT DO: Forbidden actions - anticipate and block rogue behavior
|
||||
7. CONTEXT: File paths, existing patterns, constraints
|
||||
```
|
||||
|
||||
AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
|
||||
- DOES IT WORK AS EXPECTED?
|
||||
- DOES IT FOLLOWED THE EXISTING CODEBASE PATTERN?
|
||||
- EXPECTED RESULT CAME OUT?
|
||||
- DID THE AGENT FOLLOWED "MUST DO" AND "MUST NOT DO" REQUIREMENTS?
|
||||
|
||||
**Vague prompts = rejected. Be exhaustive.**
|
||||
### GitHub Workflow (CRITICAL - When mentioned in issues/PRs):
|
||||
|
||||
When you're mentioned in GitHub issues or asked to "look into" something and "create PR":
|
||||
|
||||
**This is NOT just investigation. This is a COMPLETE WORK CYCLE.**
|
||||
|
||||
#### Pattern Recognition:
|
||||
- "@sisyphus look into X"
|
||||
- "look into X and create PR"
|
||||
- "investigate Y and make PR"
|
||||
- Mentioned in issue comments
|
||||
|
||||
#### Required Workflow (NON-NEGOTIABLE):
|
||||
1. **Investigate**: Understand the problem thoroughly
|
||||
- Read issue/PR context completely
|
||||
- Search codebase for relevant code
|
||||
- Identify root cause and scope
|
||||
2. **Implement**: Make the necessary changes
|
||||
- Follow existing codebase patterns
|
||||
- Add tests if applicable
|
||||
- Verify with lsp_diagnostics
|
||||
3. **Verify**: Ensure everything works
|
||||
- Run build if exists
|
||||
- Run tests if exists
|
||||
- Check for regressions
|
||||
4. **Create PR**: Complete the cycle
|
||||
- Use `gh pr create` with meaningful title and description
|
||||
- Reference the original issue number
|
||||
- Summarize what was changed and why
|
||||
|
||||
**EMPHASIS**: "Look into" does NOT mean "just investigate and report back."
|
||||
It means "investigate, understand, implement a solution, and create a PR."
|
||||
|
||||
**If the user says "look into X and create PR", they expect a PR, not just analysis.**
|
||||
### Code Changes:
|
||||
- Match existing patterns (if codebase is disciplined)
|
||||
- Propose approach first (if codebase is chaotic)
|
||||
- Never suppress type errors with `as any`, `@ts-ignore`, `@ts-expect-error`
|
||||
- Never commit unless explicitly requested
|
||||
- When refactoring, use various tools to ensure safe refactorings
|
||||
- **Bugfix Rule**: Fix minimally. NEVER refactor while fixing.
|
||||
|
||||
### Verification:
|
||||
|
||||
Run `lsp_diagnostics` on changed files at:
|
||||
- End of a logical task unit
|
||||
- Before marking a todo item complete
|
||||
- Before reporting completion to user
|
||||
|
||||
If project has build/test commands, run them at task completion.
|
||||
|
||||
### Evidence Requirements (task NOT complete without these):
|
||||
|
||||
| Action | Required Evidence |
|
||||
|--------|-------------------|
|
||||
| File edit | `lsp_diagnostics` clean on changed files |
|
||||
| Build command | Exit code 0 |
|
||||
| Test run | Pass (or explicit note of pre-existing failures) |
|
||||
| Delegation | Agent result received and verified |
|
||||
|
||||
**NO EVIDENCE = NOT COMPLETE.**
|
||||
---
|
||||
## Phase 2C - Failure Recovery
|
||||
|
||||
### When Fixes Fail:
|
||||
|
||||
1. Fix root causes, not symptoms
|
||||
2. Re-verify after EVERY fix attempt
|
||||
3. Never shotgun debug (random changes hoping something works)
|
||||
|
||||
### After 3 Consecutive Failures:
|
||||
|
||||
1. **STOP** all further edits immediately
|
||||
2. **REVERT** to last known working state (git checkout / undo edits)
|
||||
3. **DOCUMENT** what was attempted and what failed
|
||||
4. **CONSULT** Oracle with full failure context
|
||||
5. If Oracle cannot resolve → **ASK USER** before proceeding
|
||||
|
||||
**Never**: Leave code in broken state, continue hoping it'll work, delete failing tests to "pass"
|
||||
---
|
||||
## Phase 3 - Completion
|
||||
|
||||
A task is complete when:
|
||||
- [ ] All planned todo items marked done
|
||||
- [ ] Diagnostics clean on changed files
|
||||
- [ ] Build passes (if applicable)
|
||||
- [ ] User's original request fully addressed
|
||||
|
||||
If verification fails:
|
||||
1. Fix issues caused by your changes
|
||||
2. Do NOT fix pre-existing issues unless asked
|
||||
3. Report: "Done. Note: found N pre-existing lint errors unrelated to my changes."
|
||||
|
||||
### Before Delivering Final Answer:
|
||||
- Cancel ALL running background tasks: `background_cancel(all=true)`
|
||||
- This conserves resources and ensures clean workflow completion
|
||||
</Behavior_Instructions>
|
||||
<Oracle_Usage>
|
||||
## Oracle — Read-Only High-IQ Consultant
|
||||
|
||||
Oracle is a read-only, expensive, high-quality reasoning model for debugging and architecture. Consultation only.
|
||||
|
||||
### WHEN to Consult:
|
||||
|
||||
| Trigger | Action |
|
||||
|---------|--------|
|
||||
| Complex architecture design | Oracle FIRST, then implement |
|
||||
| After completing significant work | Oracle FIRST, then implement |
|
||||
| 2+ failed fix attempts | Oracle FIRST, then implement |
|
||||
| Unfamiliar code patterns | Oracle FIRST, then implement |
|
||||
| Security/performance concerns | Oracle FIRST, then implement |
|
||||
| Multi-system tradeoffs | Oracle FIRST, then implement |
|
||||
|
||||
### WHEN NOT to Consult:
|
||||
|
||||
- Simple file operations (use direct tools)
|
||||
- First attempt at any fix (try yourself first)
|
||||
- Questions answerable from code you've read
|
||||
- Trivial decisions (variable names, formatting)
|
||||
- Things you can infer from existing code patterns
|
||||
|
||||
### Usage Pattern:
|
||||
Briefly announce "Consulting Oracle for [reason]" before invocation.
|
||||
|
||||
**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
|
||||
</Oracle_Usage>
|
||||
<Task_Management>
|
||||
## Todo Management (CRITICAL)
|
||||
|
||||
**DEFAULT BEHAVIOR**: Create todos BEFORE starting any non-trivial task. This is your PRIMARY coordination mechanism.
|
||||
|
||||
### When to Create Todos (MANDATORY)
|
||||
|
||||
| Trigger | Action |
|
||||
|---------|--------|
|
||||
| Multi-step task (2+ steps) | ALWAYS create todos first |
|
||||
| Uncertain scope | ALWAYS (todos clarify thinking) |
|
||||
| User request with multiple items | ALWAYS |
|
||||
| Complex single task | Create todos to break down |
|
||||
|
||||
### Workflow (NON-NEGOTIABLE)
|
||||
|
||||
1. **IMMEDIATELY on receiving request**: `todowrite` to plan atomic steps.
|
||||
- ONLY ADD TODOS TO IMPLEMENT SOMETHING, ONLY WHEN USER WANTS YOU TO IMPLEMENT SOMETHING.
|
||||
2. **Before starting each step**: Mark `in_progress` (only ONE at a time)
|
||||
3. **After completing each step**: Mark `completed` IMMEDIATELY (NEVER batch)
|
||||
4. **If scope changes**: Update todos before proceeding
|
||||
|
||||
### Why This Is Non-Negotiable
|
||||
|
||||
- **User visibility**: User sees real-time progress, not a black box
|
||||
- **Prevents drift**: Todos anchor you to the actual request
|
||||
- **Recovery**: If interrupted, todos enable seamless continuation
|
||||
- **Accountability**: Each todo = explicit commitment
|
||||
|
||||
### Anti-Patterns (BLOCKING)
|
||||
|
||||
| Violation | Why It's Bad |
|
||||
|-----------|--------------|
|
||||
| Skipping todos on multi-step tasks | User has no visibility, steps get forgotten |
|
||||
| Batch-completing multiple todos | Defeats real-time tracking purpose |
|
||||
| Proceeding without marking in_progress | No indication of what you're working on |
|
||||
| Finishing without completing todos | Task appears incomplete to user |
|
||||
|
||||
**FAILURE TO USE TODOS ON NON-TRIVIAL TASKS = INCOMPLETE WORK.**
|
||||
|
||||
### Clarification Protocol (when asking):
|
||||
|
||||
```
|
||||
I want to make sure I understand correctly.
|
||||
|
||||
**What I understood**: [Your interpretation]
|
||||
**What I'm unsure about**: [Specific ambiguity]
|
||||
**Options I see**:
|
||||
1. [Option A] - [effort/implications]
|
||||
2. [Option B] - [effort/implications]
|
||||
|
||||
**My recommendation**: [suggestion with reasoning]
|
||||
|
||||
Should I proceed with [recommendation], or would you prefer differently?
|
||||
```
|
||||
</Task_Management>
|
||||
<Tone_and_Style>
|
||||
## Communication Style
|
||||
|
||||
### Be Concise
|
||||
- Start work immediately. No acknowledgments ("I'm on it", "Let me...", "I'll start...")
|
||||
- Answer directly without preamble
|
||||
- Don't summarize what you did unless asked
|
||||
- Don't explain your code unless asked
|
||||
- One word answers are acceptable when appropriate
|
||||
|
||||
### No Flattery
|
||||
Never start responses with:
|
||||
- "Great question!"
|
||||
- "That's a really good idea!"
|
||||
- "Excellent choice!"
|
||||
- Any praise of the user's input
|
||||
|
||||
Just respond directly to the substance.
|
||||
|
||||
### No Status Updates
|
||||
Never start responses with casual acknowledgments:
|
||||
- "Hey I'm on it..."
|
||||
- "I'm working on this..."
|
||||
- "Let me start by..."
|
||||
- "I'll get to work on..."
|
||||
- "I'm going to..."
|
||||
|
||||
Just start working. Use todos for progress tracking—that's what they're for.
|
||||
|
||||
### When User is Wrong
|
||||
If the user's approach seems problematic:
|
||||
- Don't blindly implement it
|
||||
- Don't lecture or be preachy
|
||||
- Concisely state your concern and alternative
|
||||
- Ask if they want to proceed anyway
|
||||
|
||||
### Match User's Style
|
||||
- If user is terse, be terse
|
||||
- If user wants detail, provide detail
|
||||
- Adapt to their communication preference
|
||||
</Tone_and_Style>
|
||||
<Constraints>
|
||||
## Hard Blocks (NEVER violate)
|
||||
|
||||
| Constraint | No Exceptions |
|
||||
|------------|---------------|
|
||||
| Type error suppression (`as any`, `@ts-ignore`) | Never |
|
||||
| Commit without explicit request | Never |
|
||||
| Speculate about unread code | Never |
|
||||
| Leave code in broken state after failures | Never |
|
||||
| Delegate without evaluating available skills | Never - MUST justify skill omissions |
|
||||
## Anti-Patterns (BLOCKING violations)
|
||||
|
||||
| Category | Forbidden |
|
||||
|----------|-----------|
|
||||
| **Type Safety** | `as any`, `@ts-ignore`, `@ts-expect-error` |
|
||||
| **Error Handling** | Empty catch blocks `catch(e) {}` |
|
||||
| **Testing** | Deleting failing tests to "pass" |
|
||||
| **Search** | Firing agents for single-line typos or obvious syntax errors |
|
||||
| **Delegation** | Using `load_skills=[]` without justifying why no skills apply |
|
||||
| **Debugging** | Shotgun debugging, random changes |
|
||||
## Soft Guidelines
|
||||
|
||||
- Prefer existing libraries over new dependencies
|
||||
- Prefer small, focused changes over large refactors
|
||||
- When uncertain about scope, ask
|
||||
</Constraints>
|
||||
|
||||
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/ — Plugin Source
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/agents/ — 11 Agent Definitions
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
@@ -10,16 +10,16 @@ Agent factories following `createXXXAgent(model) → AgentConfig` pattern. Each
|
||||
|
||||
| Agent | Model | Temp | Mode | Fallback Chain | Purpose |
|
||||
|-------|-------|------|------|----------------|---------|
|
||||
| **Sisyphus** | claude-opus-4-6 | 0.1 | primary | kimi-k2.5 → glm-4.7 → gemini-3-pro | Main orchestrator, plans + delegates |
|
||||
| **Hephaestus** | gpt-5.3-codex | 0.1 | primary | NONE (required) | Autonomous deep worker |
|
||||
| **Oracle** | gpt-5.2 | 0.1 | subagent | claude-opus-4-6 → gemini-3-pro | Read-only consultation |
|
||||
| **Librarian** | glm-4.7 | 0.1 | subagent | big-pickle → claude-sonnet-4-6 | External docs/code search |
|
||||
| **Explore** | grok-code-fast-1 | 0.1 | subagent | claude-haiku-4-5 → gpt-5-nano | Contextual grep |
|
||||
| **Multimodal-Looker** | gemini-3-flash | 0.1 | subagent | gpt-5.2 → glm-4.6v → ... (6 deep) | PDF/image analysis |
|
||||
| **Metis** | claude-opus-4-6 | **0.3** | subagent | kimi-k2.5 → gpt-5.2 → gemini-3-pro | Pre-planning consultant |
|
||||
| **Momus** | gpt-5.2 | 0.1 | subagent | claude-opus-4-6 → gemini-3-pro | Plan reviewer |
|
||||
| **Atlas** | claude-sonnet-4-6 | 0.1 | primary | kimi-k2.5 → gpt-5.2 → gemini-3-pro | Todo-list orchestrator |
|
||||
| **Prometheus** | claude-opus-4-6 | 0.1 | — | kimi-k2.5 → gpt-5.2 → gemini-3-pro | Strategic planner (internal) |
|
||||
| **Sisyphus** | claude-opus-4-6 | 0.1 | all | kimi-k2.5 → glm-5 → big-pickle | Main orchestrator, plans + delegates |
|
||||
| **Hephaestus** | gpt-5.3-codex | 0.1 | all | gpt-5.2 (copilot) | Autonomous deep worker |
|
||||
| **Oracle** | gpt-5.2 | 0.1 | subagent | gemini-3.1-pro → claude-opus-4-6 | Read-only consultation |
|
||||
| **Librarian** | kimi-k2.5 | 0.1 | subagent | gemini-3-flash → gpt-5.2 → glm-4.6v | External docs/code search |
|
||||
| **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.5 → claude-haiku-4-5 → gpt-5-nano | Contextual grep |
|
||||
| **Multimodal-Looker** | gemini-3-flash | 0.1 | subagent | minimax-m2.5 → big-pickle | PDF/image analysis |
|
||||
| **Metis** | claude-opus-4-6 | **0.3** | subagent | gpt-5.2 → kimi-k2.5 → gemini-3.1-pro | Pre-planning consultant |
|
||||
| **Momus** | gpt-5.2 | 0.1 | subagent | claude-opus-4-6 → gemini-3.1-pro | Plan reviewer |
|
||||
| **Atlas** | kimi-k2.5 | 0.1 | primary | claude-sonnet-4-6 → gpt-5.2 | Todo-list orchestrator |
|
||||
| **Prometheus** | claude-opus-4-6 | 0.1 | — | kimi-k2.5 → gpt-5.2 → gemini-3.1-pro | Strategic planner (internal) |
|
||||
| **Sisyphus-Junior** | claude-sonnet-4-6 | 0.1 | all | user-configurable | Category-spawned executor |
|
||||
|
||||
## TOOL RESTRICTIONS
|
||||
|
||||
92
src/agents/anti-duplication.test.ts
Normal file
92
src/agents/anti-duplication.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
import { describe, it, expect } from "bun:test"
|
||||
import { buildAntiDuplicationSection } from "./dynamic-agent-prompt-builder"
|
||||
|
||||
describe("buildAntiDuplicationSection", () => {
|
||||
it("#given no arguments #when building anti-duplication section #then returns comprehensive rule section", () => {
|
||||
//#given: no special configuration needed
|
||||
|
||||
//#when: building the anti-duplication section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should contain the anti-duplication rule with all key concepts
|
||||
expect(result).toContain("Anti-Duplication Rule")
|
||||
expect(result).toContain("CRITICAL")
|
||||
expect(result).toContain("DO NOT perform the same search yourself")
|
||||
})
|
||||
|
||||
it("#given no arguments #when building #then explicitly forbids manual re-search after delegation", () => {
|
||||
//#given: no special configuration
|
||||
|
||||
//#when: building the section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should explicitly list forbidden behaviors
|
||||
expect(result).toContain("FORBIDDEN")
|
||||
expect(result).toContain("manually grep/search for the same information")
|
||||
expect(result).toContain("Re-doing the research")
|
||||
})
|
||||
|
||||
it("#given no arguments #when building #then allows non-overlapping work", () => {
|
||||
//#given: no special configuration
|
||||
|
||||
//#when: building the section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should explicitly allow non-overlapping work
|
||||
expect(result).toContain("ALLOWED")
|
||||
expect(result).toContain("non-overlapping work")
|
||||
expect(result).toContain("work that doesn't depend on the delegated research")
|
||||
})
|
||||
|
||||
it("#given no arguments #when building #then includes wait-for-results instructions", () => {
|
||||
//#given: no special configuration
|
||||
|
||||
//#when: building the section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should include instructions for waiting properly
|
||||
expect(result).toContain("Wait for Results Properly")
|
||||
expect(result).toContain("End your response")
|
||||
expect(result).toContain("Wait for the completion notification")
|
||||
expect(result).toContain("background_output")
|
||||
})
|
||||
|
||||
it("#given no arguments #when building #then explains why this matters", () => {
|
||||
//#given: no special configuration
|
||||
|
||||
//#when: building the section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should explain the purpose
|
||||
expect(result).toContain("Why This Matters")
|
||||
expect(result).toContain("Wasted tokens")
|
||||
expect(result).toContain("Confusion")
|
||||
expect(result).toContain("Efficiency")
|
||||
})
|
||||
|
||||
it("#given no arguments #when building #then provides code examples", () => {
|
||||
//#given: no special configuration
|
||||
|
||||
//#when: building the section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should include examples
|
||||
expect(result).toContain("Example")
|
||||
expect(result).toContain("WRONG")
|
||||
expect(result).toContain("CORRECT")
|
||||
expect(result).toContain("task(subagent_type=")
|
||||
})
|
||||
|
||||
it("#given no arguments #when building #then uses proper markdown formatting", () => {
|
||||
//#given: no special configuration
|
||||
|
||||
//#when: building the section
|
||||
const result = buildAntiDuplicationSection()
|
||||
|
||||
//#then: should be wrapped in Anti_Duplication tag
|
||||
expect(result).toContain("<Anti_Duplication>")
|
||||
expect(result).toContain("</Anti_Duplication>")
|
||||
})
|
||||
})
|
||||
118
src/agents/atlas/atlas-prompt.test.ts
Normal file
118
src/agents/atlas/atlas-prompt.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { describe, test, expect } from "bun:test"
|
||||
import { ATLAS_SYSTEM_PROMPT } from "./default"
|
||||
import { ATLAS_GPT_SYSTEM_PROMPT } from "./gpt"
|
||||
import { ATLAS_GEMINI_SYSTEM_PROMPT } from "./gemini"
|
||||
|
||||
describe("Atlas prompts auto-continue policy", () => {
|
||||
test("default variant should forbid asking user for continuation confirmation", () => {
|
||||
// given
|
||||
const prompt = ATLAS_SYSTEM_PROMPT
|
||||
|
||||
// when
|
||||
const lowerPrompt = prompt.toLowerCase()
|
||||
|
||||
// then
|
||||
expect(lowerPrompt).toContain("auto-continue policy")
|
||||
expect(lowerPrompt).toContain("never ask the user")
|
||||
expect(lowerPrompt).toContain("should i continue")
|
||||
expect(lowerPrompt).toContain("proceed to next task")
|
||||
expect(lowerPrompt).toContain("approval-style")
|
||||
expect(lowerPrompt).toContain("auto-continue immediately")
|
||||
})
|
||||
|
||||
test("gpt variant should forbid asking user for continuation confirmation", () => {
|
||||
// given
|
||||
const prompt = ATLAS_GPT_SYSTEM_PROMPT
|
||||
|
||||
// when
|
||||
const lowerPrompt = prompt.toLowerCase()
|
||||
|
||||
// then
|
||||
expect(lowerPrompt).toContain("auto-continue policy")
|
||||
expect(lowerPrompt).toContain("never ask the user")
|
||||
expect(lowerPrompt).toContain("should i continue")
|
||||
expect(lowerPrompt).toContain("proceed to next task")
|
||||
expect(lowerPrompt).toContain("approval-style")
|
||||
expect(lowerPrompt).toContain("auto-continue immediately")
|
||||
})
|
||||
|
||||
test("gemini variant should forbid asking user for continuation confirmation", () => {
|
||||
// given
|
||||
const prompt = ATLAS_GEMINI_SYSTEM_PROMPT
|
||||
|
||||
// when
|
||||
const lowerPrompt = prompt.toLowerCase()
|
||||
|
||||
// then
|
||||
expect(lowerPrompt).toContain("auto-continue policy")
|
||||
expect(lowerPrompt).toContain("never ask the user")
|
||||
expect(lowerPrompt).toContain("should i continue")
|
||||
expect(lowerPrompt).toContain("proceed to next task")
|
||||
expect(lowerPrompt).toContain("approval-style")
|
||||
expect(lowerPrompt).toContain("auto-continue immediately")
|
||||
})
|
||||
|
||||
test("all variants should require immediate continuation after verification passes", () => {
|
||||
// given
|
||||
const prompts = [ATLAS_SYSTEM_PROMPT, ATLAS_GPT_SYSTEM_PROMPT, ATLAS_GEMINI_SYSTEM_PROMPT]
|
||||
|
||||
// when / then
|
||||
for (const prompt of prompts) {
|
||||
const lowerPrompt = prompt.toLowerCase()
|
||||
expect(lowerPrompt).toMatch(/auto-continue immediately after verification/)
|
||||
expect(lowerPrompt).toMatch(/immediately delegate next task/)
|
||||
}
|
||||
})
|
||||
|
||||
test("all variants should define when user interaction is actually needed", () => {
|
||||
// given
|
||||
const prompts = [ATLAS_SYSTEM_PROMPT, ATLAS_GPT_SYSTEM_PROMPT, ATLAS_GEMINI_SYSTEM_PROMPT]
|
||||
|
||||
// when / then
|
||||
for (const prompt of prompts) {
|
||||
const lowerPrompt = prompt.toLowerCase()
|
||||
expect(lowerPrompt).toMatch(/only pause.*truly blocked/)
|
||||
expect(lowerPrompt).toMatch(/plan needs clarification|blocked by external/)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("Atlas prompts plan path consistency", () => {
|
||||
test("default variant should use .sisyphus/plans/{plan-name}.md path", () => {
|
||||
// given
|
||||
const prompt = ATLAS_SYSTEM_PROMPT
|
||||
|
||||
// when / then
|
||||
expect(prompt).toContain(".sisyphus/plans/{plan-name}.md")
|
||||
expect(prompt).not.toContain(".sisyphus/tasks/{plan-name}.yaml")
|
||||
expect(prompt).not.toContain(".sisyphus/tasks/")
|
||||
})
|
||||
|
||||
test("gpt variant should use .sisyphus/plans/{plan-name}.md path", () => {
|
||||
// given
|
||||
const prompt = ATLAS_GPT_SYSTEM_PROMPT
|
||||
|
||||
// when / then
|
||||
expect(prompt).toContain(".sisyphus/plans/{plan-name}.md")
|
||||
expect(prompt).not.toContain(".sisyphus/tasks/")
|
||||
})
|
||||
|
||||
test("gemini variant should use .sisyphus/plans/{plan-name}.md path", () => {
|
||||
// given
|
||||
const prompt = ATLAS_GEMINI_SYSTEM_PROMPT
|
||||
|
||||
// when / then
|
||||
expect(prompt).toContain(".sisyphus/plans/{plan-name}.md")
|
||||
expect(prompt).not.toContain(".sisyphus/tasks/")
|
||||
})
|
||||
|
||||
test("all variants should read plan file after verification", () => {
|
||||
// given
|
||||
const prompts = [ATLAS_SYSTEM_PROMPT, ATLAS_GPT_SYSTEM_PROMPT, ATLAS_GEMINI_SYSTEM_PROMPT]
|
||||
|
||||
// when / then
|
||||
for (const prompt of prompts) {
|
||||
expect(prompt).toMatch(/read[\s\S]*?\.sisyphus\/plans\//)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -99,6 +99,29 @@ Every \`task()\` prompt MUST include ALL 6 sections:
|
||||
**If your prompt is under 30 lines, it's TOO SHORT.**
|
||||
</delegation_system>
|
||||
|
||||
<auto_continue>
|
||||
## AUTO-CONTINUE POLICY (STRICT)
|
||||
|
||||
**CRITICAL: NEVER ask the user "should I continue", "proceed to next task", or any approval-style questions between plan steps.**
|
||||
|
||||
**You MUST auto-continue immediately after verification passes:**
|
||||
- After any delegation completes and passes verification → Immediately delegate next task
|
||||
- Do NOT wait for user input, do NOT ask "should I continue"
|
||||
- Only pause or ask if you are truly blocked by missing information, an external dependency, or a critical failure
|
||||
|
||||
**The only time you ask the user:**
|
||||
- Plan needs clarification or modification before execution
|
||||
- Blocked by an external dependency beyond your control
|
||||
- Critical failure prevents any further progress
|
||||
|
||||
**Auto-continue examples:**
|
||||
- Task A done → Verify → Pass → Immediately start Task B
|
||||
- Task fails → Retry 3x → Still fails → Document → Move to next independent task
|
||||
- NEVER: "Should I continue to the next task?"
|
||||
|
||||
**This is NOT optional. This is core to your role as orchestrator.**
|
||||
</auto_continue>
|
||||
|
||||
<workflow>
|
||||
## Step 0: Register Tracking
|
||||
|
||||
@@ -214,7 +237,7 @@ After EVERY delegation, complete ALL of these steps — no shortcuts:
|
||||
|
||||
After verification, READ the plan file directly — every time, no exceptions:
|
||||
\`\`\`
|
||||
Read(".sisyphus/tasks/{plan-name}.yaml")
|
||||
Read(".sisyphus/plans/{plan-name}.md")
|
||||
\`\`\`
|
||||
Count remaining \`- [ ]\` tasks. This is your ground truth for what comes next.
|
||||
|
||||
|
||||
@@ -116,6 +116,29 @@ Every \`task()\` prompt MUST include ALL 6 sections:
|
||||
**Minimum 30 lines per delegation prompt. Under 30 lines = the subagent WILL fail.**
|
||||
</delegation_system>
|
||||
|
||||
<auto_continue>
|
||||
## AUTO-CONTINUE POLICY (STRICT)
|
||||
|
||||
**CRITICAL: NEVER ask the user "should I continue", "proceed to next task", or any approval-style questions between plan steps.**
|
||||
|
||||
**You MUST auto-continue immediately after verification passes:**
|
||||
- After any delegation completes and passes verification → Immediately delegate next task
|
||||
- Do NOT wait for user input, do NOT ask "should I continue"
|
||||
- Only pause or ask if you are truly blocked by missing information, an external dependency, or a critical failure
|
||||
|
||||
**The only time you ask the user:**
|
||||
- Plan needs clarification or modification before execution
|
||||
- Blocked by an external dependency beyond your control
|
||||
- Critical failure prevents any further progress
|
||||
|
||||
**Auto-continue examples:**
|
||||
- Task A done → Verify → Pass → Immediately start Task B
|
||||
- Task fails → Retry 3x → Still fails → Document → Move to next independent task
|
||||
- NEVER: "Should I continue to the next task?"
|
||||
|
||||
**This is NOT optional. This is core to your role as orchestrator.**
|
||||
</auto_continue>
|
||||
|
||||
<workflow>
|
||||
## Step 0: Register Tracking
|
||||
|
||||
|
||||
@@ -48,9 +48,10 @@ Complete ALL tasks in a work plan via \`task()\` until fully done.
|
||||
</scope_and_design_constraints>
|
||||
|
||||
<uncertainty_and_ambiguity>
|
||||
- If a task is ambiguous or underspecified:
|
||||
- During initial plan analysis, if a task is ambiguous or underspecified:
|
||||
- Ask 1-3 precise clarifying questions, OR
|
||||
- State your interpretation explicitly and proceed with the simplest approach.
|
||||
- Once execution has started, do NOT stop to ask for continuation or approval between steps.
|
||||
- Never fabricate task details, file paths, or requirements.
|
||||
- Prefer language like "Based on the plan..." instead of absolute claims.
|
||||
- When unsure about parallelization, default to sequential execution.
|
||||
@@ -134,6 +135,29 @@ Every \`task()\` prompt MUST include ALL 6 sections:
|
||||
**Minimum 30 lines per delegation prompt.**
|
||||
</delegation_system>
|
||||
|
||||
<auto_continue>
|
||||
## AUTO-CONTINUE POLICY (STRICT)
|
||||
|
||||
**CRITICAL: NEVER ask the user "should I continue", "proceed to next task", or any approval-style questions between plan steps.**
|
||||
|
||||
**You MUST auto-continue immediately after verification passes:**
|
||||
- After any delegation completes and passes verification → Immediately delegate next task
|
||||
- Do NOT wait for user input, do NOT ask "should I continue"
|
||||
- Only pause or ask if you are truly blocked by missing information, an external dependency, or a critical failure
|
||||
|
||||
**The only time you ask the user:**
|
||||
- Plan needs clarification or modification before execution
|
||||
- Blocked by an external dependency beyond your control
|
||||
- Critical failure prevents any further progress
|
||||
|
||||
**Auto-continue examples:**
|
||||
- Task A done → Verify → Pass → Immediately start Task B
|
||||
- Task fails → Retry 3x → Still fails → Document → Move to next independent task
|
||||
- NEVER: "Should I continue to the next task?"
|
||||
|
||||
**This is NOT optional. This is core to your role as orchestrator.**
|
||||
</auto_continue>
|
||||
|
||||
<workflow>
|
||||
## Step 0: Register Tracking
|
||||
|
||||
|
||||
88
src/agents/delegation-trust-prompt.test.ts
Normal file
88
src/agents/delegation-trust-prompt.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { createSisyphusAgent } from "./sisyphus"
|
||||
import { createHephaestusAgent } from "./hephaestus"
|
||||
import { buildSisyphusJuniorPrompt } from "./sisyphus-junior/agent"
|
||||
import {
|
||||
buildAntiDuplicationSection,
|
||||
buildExploreSection,
|
||||
type AvailableAgent,
|
||||
} from "./dynamic-agent-prompt-builder"
|
||||
|
||||
const exploreAgent = {
|
||||
name: "explore",
|
||||
description: "Contextual grep specialist",
|
||||
metadata: {
|
||||
category: "advisor",
|
||||
cost: "FREE",
|
||||
promptAlias: "Explore",
|
||||
triggers: [],
|
||||
useWhen: ["Multiple search angles needed"],
|
||||
avoidWhen: ["Single keyword search is enough"],
|
||||
},
|
||||
} satisfies AvailableAgent
|
||||
|
||||
describe("delegation trust prompt rules", () => {
|
||||
test("buildAntiDuplicationSection explains overlap is forbidden", () => {
|
||||
// given
|
||||
const section = buildAntiDuplicationSection()
|
||||
|
||||
// when / then
|
||||
expect(section).toContain("DO NOT perform the same search yourself")
|
||||
expect(section).toContain("non-overlapping work")
|
||||
expect(section).toContain("End your response")
|
||||
})
|
||||
|
||||
test("buildExploreSection includes delegation trust rule", () => {
|
||||
// given
|
||||
const agents = [exploreAgent]
|
||||
|
||||
// when
|
||||
const section = buildExploreSection(agents)
|
||||
|
||||
// then
|
||||
expect(section).toContain("Delegation Trust Rule")
|
||||
expect(section).toContain("do **not** manually perform that same search yourself")
|
||||
})
|
||||
|
||||
test("Sisyphus prompt forbids duplicate delegated exploration", () => {
|
||||
// given
|
||||
const agent = createSisyphusAgent("anthropic/claude-sonnet-4-6", [exploreAgent])
|
||||
|
||||
// when
|
||||
const prompt = agent.prompt
|
||||
|
||||
// then
|
||||
expect(prompt).toContain("Continue only with non-overlapping work")
|
||||
expect(prompt).toContain("DO NOT perform the same search yourself")
|
||||
})
|
||||
|
||||
test("Hephaestus prompt forbids duplicate delegated exploration", () => {
|
||||
// given
|
||||
const agent = createHephaestusAgent("openai/gpt-5.2", [exploreAgent])
|
||||
|
||||
// when
|
||||
const prompt = agent.prompt
|
||||
|
||||
// then
|
||||
expect(prompt).toContain("Continue only with non-overlapping work after launching background agents")
|
||||
expect(prompt).toContain("DO NOT perform the same search yourself")
|
||||
})
|
||||
|
||||
test("Sisyphus-Junior GPT prompt forbids duplicate delegated exploration", () => {
|
||||
// given
|
||||
const prompt = buildSisyphusJuniorPrompt("openai/gpt-5.2", false)
|
||||
|
||||
// when / then
|
||||
expect(prompt).toContain("continue only with non-overlapping work while they search")
|
||||
expect(prompt).toContain("DO NOT perform the same search yourself")
|
||||
})
|
||||
|
||||
test("Sisyphus-Junior Gemini prompt forbids duplicate delegated exploration", () => {
|
||||
// given
|
||||
const prompt = buildSisyphusJuniorPrompt("google/gemini-3.1-pro", false)
|
||||
|
||||
// when / then
|
||||
expect(prompt).toContain("continue only with non-overlapping work while they search")
|
||||
expect(prompt).toContain("DO NOT perform the same search yourself")
|
||||
})
|
||||
})
|
||||
@@ -118,6 +118,8 @@ export function buildExploreSection(agents: AvailableAgent[]): string {
|
||||
|
||||
Use it as a **peer tool**, not a fallback. Fire liberally.
|
||||
|
||||
**Delegation Trust Rule:** Once you fire an explore agent for a search, do **not** manually perform that same search yourself. Use direct tools only for non-overlapping work or when you intentionally skipped delegation.
|
||||
|
||||
**Use Direct Tools when:**
|
||||
${avoidWhen.map((w) => `- ${w}`).join("\n")}
|
||||
|
||||
@@ -308,6 +310,7 @@ export function buildAntiPatternsSection(): string {
|
||||
"- **Search**: Firing agents for single-line typos or obvious syntax errors",
|
||||
"- **Debugging**: Shotgun debugging, random changes",
|
||||
"- **Background Tasks**: Polling `background_output` on running tasks — end response and wait for notification",
|
||||
"- **Delegation Duplication**: Delegating exploration to explore/librarian and then manually doing the same search yourself",
|
||||
"- **Oracle**: Delivering answer without collecting Oracle results",
|
||||
]
|
||||
|
||||
@@ -409,3 +412,52 @@ export function buildUltraworkSection(
|
||||
|
||||
return lines.join("\n")
|
||||
}
|
||||
|
||||
// Anti-duplication section for agent prompts
|
||||
export function buildAntiDuplicationSection(): string {
|
||||
return `<Anti_Duplication>
|
||||
## Anti-Duplication Rule (CRITICAL)
|
||||
|
||||
Once you delegate exploration to explore/librarian agents, **DO NOT perform the same search yourself**.
|
||||
|
||||
### What this means:
|
||||
|
||||
**FORBIDDEN:**
|
||||
- After firing explore/librarian, manually grep/search for the same information
|
||||
- Re-doing the research the agents were just tasked with
|
||||
- "Just quickly checking" the same files the background agents are checking
|
||||
|
||||
**ALLOWED:**
|
||||
- Continue with **non-overlapping work** — work that doesn't depend on the delegated research
|
||||
- Work on unrelated parts of the codebase
|
||||
- Preparation work (e.g., setting up files, configs) that can proceed independently
|
||||
|
||||
### Wait for Results Properly:
|
||||
|
||||
When you need the delegated results but they're not ready:
|
||||
|
||||
1. **End your response** — do NOT continue with work that depends on those results
|
||||
2. **Wait for the completion notification** — the system will trigger your next turn
|
||||
3. **Then** collect results via \`background_output(task_id="...")\`
|
||||
4. **Do NOT** impatiently re-search the same topics while waiting
|
||||
|
||||
### Why This Matters:
|
||||
|
||||
- **Wasted tokens**: Duplicate exploration wastes your context budget
|
||||
- **Confusion**: You might contradict the agent's findings
|
||||
- **Efficiency**: The whole point of delegation is parallel throughput
|
||||
|
||||
### Example:
|
||||
|
||||
\`\`\`typescript
|
||||
// WRONG: After delegating, re-doing the search
|
||||
task(subagent_type="explore", run_in_background=true, ...)
|
||||
// Then immediately grep for the same thing yourself — FORBIDDEN
|
||||
|
||||
// CORRECT: Continue non-overlapping work
|
||||
task(subagent_type="explore", run_in_background=true, ...)
|
||||
// Work on a different, unrelated file while they search
|
||||
// End your response and wait for the notification
|
||||
\`\`\`
|
||||
</Anti_Duplication>`
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
buildOracleSection,
|
||||
buildHardBlocksSection,
|
||||
buildAntiPatternsSection,
|
||||
buildAntiDuplicationSection,
|
||||
categorizeTools,
|
||||
} from "./dynamic-agent-prompt-builder";
|
||||
|
||||
@@ -290,11 +291,13 @@ Prompt structure for each agent:
|
||||
- Fire 2-5 explore agents in parallel for any non-trivial codebase question
|
||||
- Parallelize independent file reads — don't read files one at a time
|
||||
- NEVER use \`run_in_background=false\` for explore/librarian
|
||||
- Continue your work immediately after launching background agents
|
||||
- Continue only with non-overlapping work after launching background agents
|
||||
- Collect results with \`background_output(task_id="...")\` when needed
|
||||
- BEFORE final answer, cancel DISPOSABLE tasks individually: \`background_cancel(taskId="bg_explore_xxx")\`, \`background_cancel(taskId="bg_librarian_xxx")\`
|
||||
- **NEVER use \`background_cancel(all=true)\`** — it kills tasks whose results you haven't collected yet
|
||||
|
||||
${buildAntiDuplicationSection()}
|
||||
|
||||
### Search Stop Conditions
|
||||
|
||||
STOP searching when:
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
import { resolvePromptAppend } from "../builtin-agents/resolve-file-uri"
|
||||
import { buildAntiDuplicationSection } from "../dynamic-agent-prompt-builder"
|
||||
|
||||
export function buildGeminiSisyphusJuniorPrompt(
|
||||
useTaskSystem: boolean,
|
||||
@@ -58,7 +59,7 @@ Before responding, ask yourself: What tools do I need to call? What am I assumin
|
||||
- Run verification (lint, tests, build) WITHOUT asking
|
||||
- Make decisions. Course-correct only on CONCRETE failure
|
||||
- Note assumptions in final message, not as questions mid-work
|
||||
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — keep working while they search
|
||||
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — continue only with non-overlapping work while they search
|
||||
|
||||
## Scope Discipline
|
||||
|
||||
@@ -77,13 +78,15 @@ Before responding, ask yourself: What tools do I need to call? What am I assumin
|
||||
|
||||
<tool_usage_rules>
|
||||
- Parallelize independent tool calls: multiple file reads, grep searches, agent fires — all at once
|
||||
- Explore/Librarian via call_omo_agent = background research. Fire them and keep working
|
||||
- Explore/Librarian via call_omo_agent = background research. Fire them and continue only with non-overlapping work
|
||||
- After any file edit: restate what changed, where, and what validation follows
|
||||
- Prefer tools over guessing whenever you need specific data (files, configs, patterns)
|
||||
- ALWAYS use tools over internal knowledge for file contents, project state, and verification
|
||||
- **DO NOT SKIP tool calls because you think you already know the answer. You DON'T.**
|
||||
</tool_usage_rules>
|
||||
|
||||
${buildAntiDuplicationSection()}
|
||||
|
||||
${taskDiscipline}
|
||||
|
||||
## Progress Updates
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { resolvePromptAppend } from "../builtin-agents/resolve-file-uri"
|
||||
import { buildAntiDuplicationSection } from "../dynamic-agent-prompt-builder"
|
||||
|
||||
export function buildGptSisyphusJuniorPrompt(
|
||||
useTaskSystem: boolean,
|
||||
@@ -40,7 +41,7 @@ When blocked: try a different approach → decompose the problem → challenge a
|
||||
- Run verification (lint, tests, build) WITHOUT asking
|
||||
- Make decisions. Course-correct only on CONCRETE failure
|
||||
- Note assumptions in final message, not as questions mid-work
|
||||
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — keep working while they search
|
||||
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — continue only with non-overlapping work while they search
|
||||
|
||||
## Scope Discipline
|
||||
|
||||
@@ -58,12 +59,14 @@ When blocked: try a different approach → decompose the problem → challenge a
|
||||
|
||||
<tool_usage_rules>
|
||||
- Parallelize independent tool calls: multiple file reads, grep searches, agent fires — all at once
|
||||
- Explore/Librarian via call_omo_agent = background research. Fire them and keep working
|
||||
- Explore/Librarian via call_omo_agent = background research. Fire them and continue only with non-overlapping work
|
||||
- After any file edit: restate what changed, where, and what validation follows
|
||||
- Prefer tools over guessing whenever you need specific data (files, configs, patterns)
|
||||
- ALWAYS use tools over internal knowledge for file contents, project state, and verification
|
||||
</tool_usage_rules>
|
||||
|
||||
${buildAntiDuplicationSection()}
|
||||
|
||||
${taskDiscipline}
|
||||
|
||||
## Progress Updates
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
buildAntiPatternsSection,
|
||||
buildDeepParallelSection,
|
||||
buildNonClaudePlannerSection,
|
||||
buildAntiDuplicationSection,
|
||||
categorizeTools,
|
||||
} from "./dynamic-agent-prompt-builder";
|
||||
|
||||
@@ -333,7 +334,7 @@ task(subagent_type="explore", run_in_background=true, load_skills=[], descriptio
|
||||
// Reference Grep (external)
|
||||
task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find JWT security docs", prompt="I'm implementing JWT auth and need current security best practices to choose token storage (httpOnly cookies vs localStorage) and set expiration policy. Find: OWASP auth guidelines, recommended token lifetimes, refresh token rotation strategies, common JWT vulnerabilities. Skip 'what is JWT' tutorials — production security guidance only.")
|
||||
task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find Express auth patterns", prompt="I'm building Express auth middleware and need production-quality patterns to structure my middleware chain. Find how established Express apps (1000+ stars) handle: middleware ordering, token refresh, role-based access control, auth error propagation. Skip basic tutorials — I need battle-tested patterns with proper error handling.")
|
||||
// Continue working immediately. System notifies on completion — collect with background_output then.
|
||||
// Continue only with non-overlapping work. System notifies on completion — collect with background_output then.
|
||||
|
||||
// WRONG: Sequential or blocking
|
||||
result = task(..., run_in_background=false) // Never wait synchronously for explore/librarian
|
||||
@@ -341,11 +342,13 @@ result = task(..., run_in_background=false) // Never wait synchronously for exp
|
||||
|
||||
### Background Result Collection:
|
||||
1. Launch parallel agents \u2192 receive task_ids
|
||||
2. Continue immediate work
|
||||
2. Continue only with non-overlapping work
|
||||
3. System sends \`<system-reminder>\` on each task completion — then call \`background_output(task_id="...")\`
|
||||
4. Need results not yet ready? **End your response.** The notification will trigger your next turn.
|
||||
5. Cleanup: Cancel disposable tasks individually via \`background_cancel(taskId="...")\`
|
||||
|
||||
${buildAntiDuplicationSection()}
|
||||
|
||||
### Search Stop Conditions
|
||||
|
||||
STOP searching when:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/cli/ — CLI: install, run, doctor, mcp-oauth
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ exports[`generateModelConfig single native provider uses Claude models when only
|
||||
"variant": "max",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "anthropic/claude-haiku-4-5",
|
||||
"model": "opencode/glm-4.7-free",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "anthropic/claude-opus-4-6",
|
||||
@@ -145,7 +145,7 @@ exports[`generateModelConfig single native provider uses Claude models with isMa
|
||||
"variant": "max",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "anthropic/claude-haiku-4-5",
|
||||
"model": "opencode/glm-4.7-free",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "anthropic/claude-opus-4-6",
|
||||
@@ -212,7 +212,8 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "openai/gpt-5.2",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
@@ -279,7 +280,8 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "openai/gpt-5.2",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
@@ -468,7 +470,8 @@ exports[`generateModelConfig all native providers uses preferred models from fal
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "google/gemini-3-flash-preview",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
@@ -542,7 +545,8 @@ exports[`generateModelConfig all native providers uses preferred models with isM
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "google/gemini-3-flash-preview",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
@@ -596,7 +600,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
||||
"agents": {
|
||||
"atlas": {
|
||||
"model": "opencode/kimi-k2.5-free",
|
||||
"model": "opencode/claude-sonnet-4-5",
|
||||
},
|
||||
"explore": {
|
||||
"model": "opencode/claude-haiku-4-5",
|
||||
@@ -617,7 +621,8 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "opencode/gemini-3-flash",
|
||||
"model": "opencode/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "opencode/gpt-5.2",
|
||||
@@ -670,7 +675,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
||||
"agents": {
|
||||
"atlas": {
|
||||
"model": "opencode/kimi-k2.5-free",
|
||||
"model": "opencode/claude-sonnet-4-5",
|
||||
},
|
||||
"explore": {
|
||||
"model": "opencode/claude-haiku-4-5",
|
||||
@@ -691,7 +696,8 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "opencode/gemini-3-flash",
|
||||
"model": "opencode/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "opencode/gpt-5.2",
|
||||
@@ -988,7 +994,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
||||
"agents": {
|
||||
"atlas": {
|
||||
"model": "opencode/kimi-k2.5-free",
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
},
|
||||
"explore": {
|
||||
"model": "anthropic/claude-haiku-4-5",
|
||||
@@ -1009,7 +1015,8 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "opencode/gemini-3-flash",
|
||||
"model": "opencode/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "opencode/gpt-5.2",
|
||||
@@ -1083,7 +1090,8 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "github-copilot/gemini-3-flash-preview",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
@@ -1263,7 +1271,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
||||
"agents": {
|
||||
"atlas": {
|
||||
"model": "opencode/kimi-k2.5-free",
|
||||
"model": "github-copilot/claude-sonnet-4.5",
|
||||
},
|
||||
"explore": {
|
||||
"model": "opencode/claude-haiku-4-5",
|
||||
@@ -1284,7 +1292,8 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "github-copilot/gemini-3-flash-preview",
|
||||
"model": "opencode/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "github-copilot/gpt-5.2",
|
||||
@@ -1337,7 +1346,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
||||
"agents": {
|
||||
"atlas": {
|
||||
"model": "opencode/kimi-k2.5-free",
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
},
|
||||
"explore": {
|
||||
"model": "anthropic/claude-haiku-4-5",
|
||||
@@ -1358,7 +1367,8 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "google/gemini-3-flash-preview",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
@@ -1411,7 +1421,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
||||
"agents": {
|
||||
"atlas": {
|
||||
"model": "opencode/kimi-k2.5-free",
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
},
|
||||
"explore": {
|
||||
"model": "anthropic/claude-haiku-4-5",
|
||||
@@ -1432,7 +1442,8 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
|
||||
"variant": "medium",
|
||||
},
|
||||
"multimodal-looker": {
|
||||
"model": "google/gemini-3-flash-preview",
|
||||
"model": "openai/gpt-5.3-codex",
|
||||
"variant": "medium",
|
||||
},
|
||||
"oracle": {
|
||||
"model": "openai/gpt-5.2",
|
||||
|
||||
@@ -323,8 +323,8 @@ describe("generateOmoConfig - model fallback system", () => {
|
||||
expect((result.agents as Record<string, { model: string }>).sisyphus).toBeUndefined()
|
||||
// #then Oracle should use native OpenAI (first fallback entry)
|
||||
expect((result.agents as Record<string, { model: string }>).oracle.model).toBe("openai/gpt-5.2")
|
||||
// #then multimodal-looker should use native OpenAI (fallback within native tier)
|
||||
expect((result.agents as Record<string, { model: string }>)["multimodal-looker"].model).toBe("openai/gpt-5.2")
|
||||
// #then multimodal-looker should use native OpenAI (first fallback entry is gpt-5.3-codex)
|
||||
expect((result.agents as Record<string, { model: string }>)["multimodal-looker"].model).toBe("openai/gpt-5.3-codex")
|
||||
})
|
||||
|
||||
test("uses haiku for explore when Claude max20", () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/cli/config-manager/ — CLI Installation Utilities
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ export function initConfigContext(binary: OpenCodeBinaryType, version: string |
|
||||
|
||||
export function getConfigContext(): ConfigContext {
|
||||
if (!configContext) {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.warn("[config-context] getConfigContext() called before initConfigContext(); defaulting to CLI paths.")
|
||||
}
|
||||
const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null })
|
||||
configContext = { binary: "opencode", version: null, paths }
|
||||
}
|
||||
|
||||
18
src/cli/doctor/checks/system-loaded-version.test.ts
Normal file
18
src/cli/doctor/checks/system-loaded-version.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
|
||||
import { getSuggestedInstallTag } from "./system-loaded-version"
|
||||
|
||||
describe("system loaded version", () => {
|
||||
describe("getSuggestedInstallTag", () => {
|
||||
it("returns prerelease channel when current version is prerelease", () => {
|
||||
//#given
|
||||
const currentVersion = "3.2.0-beta.4"
|
||||
|
||||
//#when
|
||||
const installTag = getSuggestedInstallTag(currentVersion)
|
||||
|
||||
//#then
|
||||
expect(installTag).toBe("beta")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -77,3 +77,7 @@ export async function getLatestPluginVersion(currentVersion: string | null): Pro
|
||||
const channel = extractChannel(currentVersion)
|
||||
return getLatestVersion(channel)
|
||||
}
|
||||
|
||||
export function getSuggestedInstallTag(currentVersion: string | null): string {
|
||||
return extractChannel(currentVersion)
|
||||
}
|
||||
|
||||
104
src/cli/doctor/checks/system.test.ts
Normal file
104
src/cli/doctor/checks/system.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
||||
|
||||
const mockFindOpenCodeBinary = mock(async () => ({ path: "/usr/local/bin/opencode" }))
|
||||
const mockGetOpenCodeVersion = mock(async () => "1.0.200")
|
||||
const mockCompareVersions = mock(() => true)
|
||||
const mockGetPluginInfo = mock(() => ({
|
||||
registered: true,
|
||||
entry: "oh-my-opencode",
|
||||
isPinned: false,
|
||||
pinnedVersion: null,
|
||||
configPath: null,
|
||||
isLocalDev: false,
|
||||
}))
|
||||
const mockGetLoadedPluginVersion = mock(() => ({
|
||||
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
|
||||
cachePackagePath: "/tmp/package.json",
|
||||
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
|
||||
expectedVersion: "3.0.0",
|
||||
loadedVersion: "3.1.0",
|
||||
}))
|
||||
const mockGetLatestPluginVersion = mock(async () => null)
|
||||
|
||||
mock.module("./system-binary", () => ({
|
||||
findOpenCodeBinary: mockFindOpenCodeBinary,
|
||||
getOpenCodeVersion: mockGetOpenCodeVersion,
|
||||
compareVersions: mockCompareVersions,
|
||||
}))
|
||||
|
||||
mock.module("./system-plugin", () => ({
|
||||
getPluginInfo: mockGetPluginInfo,
|
||||
}))
|
||||
|
||||
mock.module("./system-loaded-version", () => ({
|
||||
getLoadedPluginVersion: mockGetLoadedPluginVersion,
|
||||
getLatestPluginVersion: mockGetLatestPluginVersion,
|
||||
}))
|
||||
|
||||
const { checkSystem } = await import("./system?test")
|
||||
|
||||
describe("system check", () => {
|
||||
beforeEach(() => {
|
||||
mockFindOpenCodeBinary.mockReset()
|
||||
mockGetOpenCodeVersion.mockReset()
|
||||
mockCompareVersions.mockReset()
|
||||
mockGetPluginInfo.mockReset()
|
||||
mockGetLoadedPluginVersion.mockReset()
|
||||
mockGetLatestPluginVersion.mockReset()
|
||||
|
||||
mockFindOpenCodeBinary.mockResolvedValue({ path: "/usr/local/bin/opencode" })
|
||||
mockGetOpenCodeVersion.mockResolvedValue("1.0.200")
|
||||
mockCompareVersions.mockReturnValue(true)
|
||||
mockGetPluginInfo.mockReturnValue({
|
||||
registered: true,
|
||||
entry: "oh-my-opencode",
|
||||
isPinned: false,
|
||||
pinnedVersion: null,
|
||||
configPath: null,
|
||||
isLocalDev: false,
|
||||
})
|
||||
mockGetLoadedPluginVersion.mockReturnValue({
|
||||
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
|
||||
cachePackagePath: "/tmp/package.json",
|
||||
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
|
||||
expectedVersion: "3.0.0",
|
||||
loadedVersion: "3.1.0",
|
||||
})
|
||||
mockGetLatestPluginVersion.mockResolvedValue(null)
|
||||
})
|
||||
|
||||
describe("#given cache directory contains spaces", () => {
|
||||
it("uses a quoted cache directory in mismatch fix command", async () => {
|
||||
//#when
|
||||
const result = await checkSystem()
|
||||
|
||||
//#then
|
||||
const mismatchIssue = result.issues.find((issue) => issue.title === "Loaded plugin version mismatch")
|
||||
expect(mismatchIssue?.fix).toBe('Reinstall: cd "/Users/test/Library/Caches/opencode with spaces" && bun install')
|
||||
})
|
||||
|
||||
it("uses the loaded version channel for update fix command", async () => {
|
||||
//#given
|
||||
mockGetLoadedPluginVersion.mockReturnValue({
|
||||
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
|
||||
cachePackagePath: "/tmp/package.json",
|
||||
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
|
||||
expectedVersion: "3.0.0-canary.1",
|
||||
loadedVersion: "3.0.0-canary.1",
|
||||
})
|
||||
mockGetLatestPluginVersion.mockResolvedValue("3.0.0-canary.2")
|
||||
mockCompareVersions.mockImplementation((leftVersion: string, rightVersion: string) => {
|
||||
return !(leftVersion === "3.0.0-canary.1" && rightVersion === "3.0.0-canary.2")
|
||||
})
|
||||
|
||||
//#when
|
||||
const result = await checkSystem()
|
||||
|
||||
//#then
|
||||
const outdatedIssue = result.issues.find((issue) => issue.title === "Loaded plugin is outdated")
|
||||
expect(outdatedIssue?.fix).toBe(
|
||||
'Update: cd "/Users/test/Library/Caches/opencode with spaces" && bun add oh-my-opencode@canary'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,7 @@ import { MIN_OPENCODE_VERSION, CHECK_IDS, CHECK_NAMES } from "../constants"
|
||||
import type { CheckResult, DoctorIssue, SystemInfo } from "../types"
|
||||
import { findOpenCodeBinary, getOpenCodeVersion, compareVersions } from "./system-binary"
|
||||
import { getPluginInfo } from "./system-plugin"
|
||||
import { getLatestPluginVersion, getLoadedPluginVersion } from "./system-loaded-version"
|
||||
import { getLatestPluginVersion, getLoadedPluginVersion, getSuggestedInstallTag } from "./system-loaded-version"
|
||||
import { parseJsonc } from "../../../shared"
|
||||
|
||||
function isConfigValid(configPath: string | null): boolean {
|
||||
@@ -54,6 +54,7 @@ export async function checkSystem(): Promise<CheckResult> {
|
||||
const [systemInfo, pluginInfo] = await Promise.all([gatherSystemInfo(), Promise.resolve(getPluginInfo())])
|
||||
const loadedInfo = getLoadedPluginVersion()
|
||||
const latestVersion = await getLatestPluginVersion(systemInfo.loadedVersion)
|
||||
const installTag = getSuggestedInstallTag(systemInfo.loadedVersion)
|
||||
const issues: DoctorIssue[] = []
|
||||
|
||||
if (!systemInfo.opencodePath) {
|
||||
@@ -93,7 +94,7 @@ export async function checkSystem(): Promise<CheckResult> {
|
||||
issues.push({
|
||||
title: "Loaded plugin version mismatch",
|
||||
description: `Cache expects ${loadedInfo.expectedVersion} but loaded ${loadedInfo.loadedVersion}.`,
|
||||
fix: "Reinstall plugin dependencies in OpenCode cache",
|
||||
fix: `Reinstall: cd "${loadedInfo.cacheDir}" && bun install`,
|
||||
severity: "warning",
|
||||
affects: ["plugin loading"],
|
||||
})
|
||||
@@ -107,7 +108,7 @@ export async function checkSystem(): Promise<CheckResult> {
|
||||
issues.push({
|
||||
title: "Loaded plugin is outdated",
|
||||
description: `Loaded ${systemInfo.loadedVersion}, latest ${latestVersion}.`,
|
||||
fix: "Update: cd ~/.config/opencode && bun update oh-my-opencode",
|
||||
fix: `Update: cd "${loadedInfo.cacheDir}" && bun add oh-my-opencode@${installTag}`,
|
||||
severity: "warning",
|
||||
affects: ["plugin features"],
|
||||
})
|
||||
|
||||
@@ -9,7 +9,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
|
||||
{ providers: ["opencode"], model: "glm-4.7-free" },
|
||||
],
|
||||
@@ -44,12 +43,10 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
},
|
||||
"multimodal-looker": {
|
||||
fallbackChain: [
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
|
||||
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
|
||||
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
|
||||
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
|
||||
{ providers: ["opencode"], model: "gpt-5-nano" },
|
||||
],
|
||||
},
|
||||
@@ -57,7 +54,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
|
||||
],
|
||||
@@ -66,7 +62,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
fallbackChain: [
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
|
||||
],
|
||||
@@ -81,7 +76,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
atlas: {
|
||||
fallbackChain: [
|
||||
{ providers: ["kimi-for-coding"], model: "k2p5" },
|
||||
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
||||
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
|
||||
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/cli/run/ — Non-Interactive Session Launcher
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -318,14 +318,8 @@ describe("event handling", () => {
|
||||
// given
|
||||
const ctx = createMockContext("my-session")
|
||||
const state: EventState = {
|
||||
...createEventState(),
|
||||
mainSessionIdle: true,
|
||||
mainSessionError: false,
|
||||
lastError: null,
|
||||
lastOutput: "",
|
||||
lastPartText: "",
|
||||
currentTool: null,
|
||||
hasReceivedMeaningfulWork: false,
|
||||
messageCount: 0,
|
||||
}
|
||||
|
||||
const payload: EventPayload = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# src/config/ — Zod v4 Schema System
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
22 schema files composing `OhMyOpenCodeConfigSchema`. Zod v4 validation with `safeParse()`. All fields optional — omitted fields use plugin defaults.
|
||||
24 schema files composing `OhMyOpenCodeConfigSchema`. Zod v4 validation with `safeParse()`. All fields optional — omitted fields use plugin defaults.
|
||||
|
||||
## SCHEMA TREE
|
||||
|
||||
@@ -29,14 +29,18 @@ config/schema/
|
||||
├── git-master.ts # commit_footer: boolean | string
|
||||
├── browser-automation.ts # provider: playwright | agent-browser | playwright-cli
|
||||
├── background-task.ts # Concurrency limits per model/provider
|
||||
├── fallback-models.ts # FallbackModelsConfigSchema
|
||||
├── runtime-fallback.ts # RuntimeFallbackConfigSchema
|
||||
├── babysitting.ts # Unstable agent monitoring
|
||||
├── dynamic-context-pruning.ts # Context pruning settings
|
||||
├── start-work.ts # StartWorkConfigSchema (auto_commit)
|
||||
└── internal/permission.ts # AgentPermissionSchema
|
||||
|
||||
```
|
||||
|
||||
## ROOT SCHEMA FIELDS (27)
|
||||
## ROOT SCHEMA FIELDS (28)
|
||||
|
||||
`$schema`, `new_task_system_enabled`, `default_run_agent`, `disabled_mcps`, `disabled_agents`, `disabled_skills`, `disabled_hooks`, `disabled_commands`, `disabled_tools`, `hashline_edit`, `agents`, `categories`, `claude_code`, `sisyphus_agent`, `comment_checker`, `experimental`, `auto_update`, `skills`, `ralph_loop`, `background_task`, `notification`, `babysitting`, `git_master`, `browser_automation_engine`, `websearch`, `tmux`, `sisyphus`, `_migrations`
|
||||
`$schema`, `new_task_system_enabled`, `default_run_agent`, `disabled_mcps`, `disabled_agents`, `disabled_skills`, `disabled_hooks`, `disabled_commands`, `disabled_tools`, `hashline_edit`, `agents`, `categories`, `claude_code`, `sisyphus_agent`, `comment_checker`, `experimental`, `auto_update`, `skills`, `ralph_loop`, `background_task`, `notification`, `babysitting`, `git_master`, `browser_automation_engine`, `websearch`, `tmux`, `sisyphus`, `start_work`, `_migrations`
|
||||
|
||||
## AGENT OVERRIDE FIELDS (21)
|
||||
|
||||
|
||||
51
src/config/schema/background-task.test.ts
Normal file
51
src/config/schema/background-task.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { ZodError } from "zod/v4"
|
||||
import { BackgroundTaskConfigSchema } from "./background-task"
|
||||
|
||||
describe("BackgroundTaskConfigSchema", () => {
|
||||
describe("syncPollTimeoutMs", () => {
|
||||
describe("#given valid syncPollTimeoutMs (120000)", () => {
|
||||
test("#when parsed #then returns correct value", () => {
|
||||
const result = BackgroundTaskConfigSchema.parse({ syncPollTimeoutMs: 120000 })
|
||||
|
||||
expect(result.syncPollTimeoutMs).toBe(120000)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#given syncPollTimeoutMs below minimum (59999)", () => {
|
||||
test("#when parsed #then throws ZodError", () => {
|
||||
let thrownError: unknown
|
||||
|
||||
try {
|
||||
BackgroundTaskConfigSchema.parse({ syncPollTimeoutMs: 59999 })
|
||||
} catch (error) {
|
||||
thrownError = error
|
||||
}
|
||||
|
||||
expect(thrownError).toBeInstanceOf(ZodError)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#given syncPollTimeoutMs not provided", () => {
|
||||
test("#when parsed #then field is undefined", () => {
|
||||
const result = BackgroundTaskConfigSchema.parse({})
|
||||
|
||||
expect(result.syncPollTimeoutMs).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#given syncPollTimeoutMs is non-number ("abc")', () => {
|
||||
test("#when parsed #then throws ZodError", () => {
|
||||
let thrownError: unknown
|
||||
|
||||
try {
|
||||
BackgroundTaskConfigSchema.parse({ syncPollTimeoutMs: "abc" })
|
||||
} catch (error) {
|
||||
thrownError = error
|
||||
}
|
||||
|
||||
expect(thrownError).toBeInstanceOf(ZodError)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -8,6 +8,7 @@ export const BackgroundTaskConfigSchema = z.object({
|
||||
staleTimeoutMs: z.number().min(60000).optional(),
|
||||
/** Timeout for tasks that never received any progress update, falling back to startedAt (default: 600000 = 10 minutes, minimum: 60000 = 1 minute) */
|
||||
messageStalenessTimeoutMs: z.number().min(60000).optional(),
|
||||
syncPollTimeoutMs: z.number().min(60000).optional(),
|
||||
})
|
||||
|
||||
export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { SkillsConfigSchema } from "./skills"
|
||||
import { SisyphusConfigSchema } from "./sisyphus"
|
||||
import { SisyphusAgentConfigSchema } from "./sisyphus-agent"
|
||||
import { TmuxConfigSchema } from "./tmux"
|
||||
import { StartWorkConfigSchema } from "./start-work"
|
||||
import { WebsearchConfigSchema } from "./websearch"
|
||||
|
||||
export const OhMyOpenCodeConfigSchema = z.object({
|
||||
@@ -60,6 +61,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
|
||||
websearch: WebsearchConfigSchema.optional(),
|
||||
tmux: TmuxConfigSchema.optional(),
|
||||
sisyphus: SisyphusConfigSchema.optional(),
|
||||
start_work: StartWorkConfigSchema.optional(),
|
||||
/** Migration history to prevent re-applying migrations (e.g., model version upgrades) */
|
||||
_migrations: z.array(z.string()).optional(),
|
||||
})
|
||||
|
||||
8
src/config/schema/start-work.ts
Normal file
8
src/config/schema/start-work.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const StartWorkConfigSchema = z.object({
|
||||
/** Enable auto-commit after each atomic task completion (default: true) */
|
||||
auto_commit: z.boolean().default(true),
|
||||
})
|
||||
|
||||
export type StartWorkConfig = z.infer<typeof StartWorkConfigSchema>
|
||||
@@ -51,6 +51,7 @@ export function createHooks(args: {
|
||||
|
||||
const skill = createSkillHooks({
|
||||
ctx,
|
||||
pluginConfig,
|
||||
isHookEnabled,
|
||||
safeHookEnabled,
|
||||
mergedSkills,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/features/ — 19 Feature Modules
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# src/features/background-agent/ — Core Orchestration Engine
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
39 files (~10k LOC). Manages async task lifecycle: launch → queue → run → poll → complete/error. Concurrency limited per model/provider (default 5). Central to multi-agent orchestration.
|
||||
30 files (~10k LOC). Manages async task lifecycle: launch → queue → run → poll → complete/error. Concurrency limited per model/provider (default 5). Central to multi-agent orchestration.
|
||||
|
||||
## TASK LIFECYCLE
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/features/claude-tasks/ — Task Schema + Storage
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/features/mcp-oauth/ — OAuth 2.0 + PKCE + DCR for MCP Servers
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/features/opencode-skill-loader/ — 4-Scope Skill Discovery
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/features/tmux-subagent/ — Tmux Pane Management
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# src/hooks/ — 46 Lifecycle Hooks
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
46 hooks across 39 directories + 6 standalone files. Three-tier composition: Core(37) + Continuation(7) + Skill(2). All hooks follow `createXXXHook(deps) → HookFunction` factory pattern.
|
||||
46 hooks across 45 directories + 11 standalone files. Three-tier composition: Core(37) + Continuation(7) + Skill(2). All hooks follow `createXXXHook(deps) → HookFunction` factory pattern.
|
||||
|
||||
## HOOK TIERS
|
||||
|
||||
@@ -14,38 +14,48 @@
|
||||
hooks/
|
||||
├── atlas/ # Main orchestration (757 lines)
|
||||
├── anthropic-context-window-limit-recovery/ # Auto-summarize
|
||||
├── todo-continuation-enforcer.ts # Force TODO completion
|
||||
├── ralph-loop/ # Self-referential dev loop
|
||||
├── claude-code-hooks/ # settings.json compat layer - see AGENTS.md
|
||||
├── comment-checker/ # Prevents AI slop
|
||||
├── anthropic-effort/ # Reasoning effort level adjustment
|
||||
├── anthropic-image-context/ # Image context handling for Anthropic
|
||||
├── auto-slash-command/ # Detects /command patterns
|
||||
├── rules-injector/ # Conditional rules
|
||||
├── auto-update-checker/ # Plugin update check
|
||||
├── background-notification/ # OS notification
|
||||
├── beast-mode-system/ # Beast mode system prompt injection
|
||||
├── category-skill-reminder/ # Reminds of category skills
|
||||
├── claude-code-hooks/ # settings.json compat layer
|
||||
├── comment-checker/ # Prevents AI slop
|
||||
├── compaction-context-injector/ # Injects context on compaction
|
||||
├── compaction-todo-preserver/ # Preserves todos through compaction
|
||||
├── delegate-task-retry/ # Retries failed delegations
|
||||
├── directory-agents-injector/ # Auto-injects AGENTS.md
|
||||
├── directory-readme-injector/ # Auto-injects README.md
|
||||
├── edit-error-recovery/ # Recovers from failures
|
||||
├── thinking-block-validator/ # Ensures valid <thinking>
|
||||
├── context-window-monitor.ts # Reminds of headroom
|
||||
├── session-recovery/ # Auto-recovers from crashes
|
||||
├── think-mode/ # Dynamic thinking budget
|
||||
├── keyword-detector/ # ultrawork/search/analyze modes
|
||||
├── background-notification/ # OS notification
|
||||
├── prometheus-md-only/ # Planner read-only mode
|
||||
├── agent-usage-reminder/ # Specialized agent hints
|
||||
├── auto-update-checker/ # Plugin update check
|
||||
├── tool-output-truncator.ts # Prevents context bloat
|
||||
├── compaction-context-injector/ # Injects context on compaction
|
||||
├── delegate-task-retry/ # Retries failed delegations
|
||||
├── hashline-edit-diff-enhancer/ # Enhanced diff output for hashline edits
|
||||
├── hashline-read-enhancer/ # Adds LINE#ID hashes to Read output
|
||||
├── interactive-bash-session/ # Tmux session management
|
||||
├── json-error-recovery/ # JSON parse error correction
|
||||
├── keyword-detector/ # ultrawork/search/analyze modes
|
||||
├── model-fallback/ # Provider-level model fallback
|
||||
├── no-hephaestus-non-gpt/ # Block Hephaestus from non-GPT
|
||||
├── no-sisyphus-gpt/ # Block Sisyphus from GPT
|
||||
├── non-interactive-env/ # Non-TTY environment handling
|
||||
├── start-work/ # Sisyphus work session starter
|
||||
├── task-resume-info/ # Resume info for cancelled tasks
|
||||
├── prometheus-md-only/ # Planner read-only mode
|
||||
├── question-label-truncator/ # Auto-truncates question labels
|
||||
├── category-skill-reminder/ # Reminds of category skills
|
||||
├── empty-task-response-detector.ts # Detects empty responses
|
||||
├── sisyphus-junior-notepad/ # Sisyphus Junior notepad
|
||||
├── stop-continuation-guard/ # Guards stop continuation
|
||||
├── subagent-question-blocker/ # Blocks subagent questions
|
||||
├── ralph-loop/ # Self-referential dev loop
|
||||
├── read-image-resizer/ # Resize images for context efficiency
|
||||
├── rules-injector/ # Conditional rules
|
||||
├── runtime-fallback/ # Auto-switch models on API errors
|
||||
├── session-recovery/ # Auto-recovers from crashes
|
||||
├── sisyphus-junior-notepad/ # Sisyphus Junior notepad
|
||||
├── start-work/ # Sisyphus work session starter
|
||||
├── stop-continuation-guard/ # Guards stop continuation
|
||||
├── task-reminder/ # Task system usage reminders
|
||||
├── task-resume-info/ # Resume info for cancelled tasks
|
||||
├── tasks-todowrite-disabler/ # Disable TodoWrite when task system active
|
||||
├── think-mode/ # Dynamic thinking budget
|
||||
├── thinking-block-validator/ # Ensures valid <thinking>
|
||||
├── todo-continuation-enforcer/ # Force TODO completion
|
||||
├── unstable-agent-babysitter/ # Monitor unstable agent behavior
|
||||
├── write-existing-file-guard/ # Require Read before Write
|
||||
└── index.ts # Hook aggregation + registration
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/anthropic-context-window-limit-recovery/ — Multi-Strategy Context Recovery
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { log } from "../../shared"
|
||||
import { log, normalizeModelID } from "../../shared"
|
||||
|
||||
const OPUS_4_6_PATTERN = /claude-opus-4[-.]6/i
|
||||
|
||||
function normalizeModelID(modelID: string): string {
|
||||
return modelID.replace(/\.(\d+)/g, "-$1")
|
||||
}
|
||||
|
||||
function isClaudeProvider(providerID: string, modelID: string): boolean {
|
||||
if (["anthropic", "google-vertex-anthropic", "opencode"].includes(providerID)) return true
|
||||
if (providerID === "github-copilot" && modelID.toLowerCase().includes("claude")) return true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/atlas/ — Master Boulder Orchestrator
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { AtlasHookOptions, SessionState } from "./types"
|
||||
export function createAtlasHook(ctx: PluginInput, options?: AtlasHookOptions) {
|
||||
const sessions = new Map<string, SessionState>()
|
||||
const pendingFilePaths = new Map<string, string>()
|
||||
const autoCommit = options?.autoCommit ?? true
|
||||
|
||||
function getState(sessionID: string): SessionState {
|
||||
let state = sessions.get(sessionID)
|
||||
@@ -20,6 +21,6 @@ export function createAtlasHook(ctx: PluginInput, options?: AtlasHookOptions) {
|
||||
return {
|
||||
handler: createAtlasEventHandler({ ctx, options, sessions, getState }),
|
||||
"tool.execute.before": createToolExecuteBeforeHandler({ ctx, pendingFilePaths }),
|
||||
"tool.execute.after": createToolExecuteAfterHandler({ ctx, pendingFilePaths }),
|
||||
"tool.execute.after": createToolExecuteAfterHandler({ ctx, pendingFilePaths, autoCommit }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import type { ToolExecuteAfterInput, ToolExecuteAfterOutput } from "./types"
|
||||
export function createToolExecuteAfterHandler(input: {
|
||||
ctx: PluginInput
|
||||
pendingFilePaths: Map<string, string>
|
||||
}): (toolInput: ToolExecuteAfterInput, toolOutput: ToolExecuteAfterOutput) => Promise<void> {
|
||||
const { ctx, pendingFilePaths } = input
|
||||
|
||||
autoCommit: boolean
|
||||
}): (toolInput: ToolExecuteAfterInput, toolOutput: ToolExecuteAfterOutput) => Promise<void> {
|
||||
const { ctx, pendingFilePaths, autoCommit } = input
|
||||
return async (toolInput, toolOutput): Promise<void> => {
|
||||
// Guard against undefined output (e.g., from /review command - see issue #1035)
|
||||
if (!toolOutput) {
|
||||
@@ -76,7 +76,7 @@ export function createToolExecuteAfterHandler(input: {
|
||||
// Preserve original subagent response - critical for debugging failed tasks
|
||||
const originalResponse = toolOutput.output
|
||||
|
||||
toolOutput.output = `
|
||||
toolOutput.output = `
|
||||
## SUBAGENT WORK COMPLETED
|
||||
|
||||
${fileChanges}
|
||||
@@ -88,9 +88,8 @@ ${fileChanges}
|
||||
${originalResponse}
|
||||
|
||||
<system-reminder>
|
||||
${buildOrchestratorReminder(boulderState.plan_name, progress, subagentSessionId)}
|
||||
${buildOrchestratorReminder(boulderState.plan_name, progress, subagentSessionId, autoCommit)}
|
||||
</system-reminder>`
|
||||
|
||||
log(`[${HOOK_NAME}] Output transformed for orchestrator mode (boulder)`, {
|
||||
plan: boulderState.plan_name,
|
||||
progress: `${progress.completed}/${progress.total}`,
|
||||
|
||||
@@ -8,6 +8,8 @@ export interface AtlasHookOptions {
|
||||
backgroundManager?: BackgroundManager
|
||||
isContinuationStopped?: (sessionID: string) => boolean
|
||||
agentOverrides?: AgentOverrides
|
||||
/** Enable auto-commit after each atomic task completion (default: true) */
|
||||
autoCommit?: boolean
|
||||
}
|
||||
|
||||
export interface ToolExecuteAfterInput {
|
||||
|
||||
@@ -14,9 +14,22 @@ task(session_id="${sessionId}", prompt="fix: [describe the specific failure]")
|
||||
export function buildOrchestratorReminder(
|
||||
planName: string,
|
||||
progress: { total: number; completed: number },
|
||||
sessionId: string
|
||||
sessionId: string,
|
||||
autoCommit: boolean = true
|
||||
): string {
|
||||
const remaining = progress.total - progress.completed
|
||||
|
||||
const commitStep = autoCommit
|
||||
? `
|
||||
**STEP 8: COMMIT ATOMIC UNIT**
|
||||
|
||||
- Stage ONLY the verified changes
|
||||
- Commit with clear message describing what was done
|
||||
`
|
||||
: ""
|
||||
|
||||
const nextStepNumber = autoCommit ? 9 : 8
|
||||
|
||||
return `
|
||||
---
|
||||
|
||||
@@ -60,13 +73,8 @@ Update the plan file \`.sisyphus/plans/${planName}.md\`:
|
||||
- Use \`Edit\` tool to modify the checkbox
|
||||
|
||||
**DO THIS BEFORE ANYTHING ELSE. Unmarked = Untracked = Lost progress.**
|
||||
|
||||
**STEP 8: COMMIT ATOMIC UNIT**
|
||||
|
||||
- Stage ONLY the verified changes
|
||||
- Commit with clear message describing what was done
|
||||
|
||||
**STEP 9: PROCEED TO NEXT TASK**
|
||||
${commitStep}
|
||||
**STEP ${nextStepNumber}: PROCEED TO NEXT TASK**
|
||||
|
||||
- Read the plan file AGAIN to identify the next \`- [ ]\` task
|
||||
- Start immediately - DO NOT STOP
|
||||
|
||||
19
src/hooks/auto-slash-command/constants.test.ts
Normal file
19
src/hooks/auto-slash-command/constants.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { parseSlashCommand } from "./detector"
|
||||
|
||||
describe("slash command parsing pattern", () => {
|
||||
describe("#given plugin namespace includes dot", () => {
|
||||
it("#then parses command name with dot and colon", () => {
|
||||
// given
|
||||
const text = "/my.plugin:run ship"
|
||||
|
||||
// when
|
||||
const parsed = parseSlashCommand(text)
|
||||
|
||||
// then
|
||||
expect(parsed).not.toBeNull()
|
||||
expect(parsed?.command).toBe("my.plugin:run")
|
||||
expect(parsed?.args).toBe("ship")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -3,7 +3,7 @@ export const HOOK_NAME = "auto-slash-command" as const
|
||||
export const AUTO_SLASH_COMMAND_TAG_OPEN = "<auto-slash-command>"
|
||||
export const AUTO_SLASH_COMMAND_TAG_CLOSE = "</auto-slash-command>"
|
||||
|
||||
export const SLASH_COMMAND_PATTERN = /^\/([a-zA-Z][\w-]*)\s*(.*)/
|
||||
export const SLASH_COMMAND_PATTERN = /^\/([a-zA-Z@][\w.:@/-]*)\s*(.*)/
|
||||
|
||||
export const EXCLUDED_COMMANDS = new Set([
|
||||
"ralph-loop",
|
||||
|
||||
@@ -102,6 +102,19 @@ After`
|
||||
expect(result?.args).toBe("project")
|
||||
})
|
||||
|
||||
it("should parse namespaced marketplace commands", () => {
|
||||
// given a namespaced command
|
||||
const text = "/daplug:run-prompt build bridge"
|
||||
|
||||
// when parsing
|
||||
const result = parseSlashCommand(text)
|
||||
|
||||
// then should keep full namespaced command
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.command).toBe("daplug:run-prompt")
|
||||
expect(result?.args).toBe("build bridge")
|
||||
})
|
||||
|
||||
it("should return null for non-slash text", () => {
|
||||
// given text without slash
|
||||
const text = "regular text"
|
||||
|
||||
195
src/hooks/auto-slash-command/executor.test.ts
Normal file
195
src/hooks/auto-slash-command/executor.test.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "bun:test"
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { executeSlashCommand } from "./executor"
|
||||
|
||||
const ENV_KEYS = [
|
||||
"CLAUDE_CONFIG_DIR",
|
||||
"CLAUDE_PLUGINS_HOME",
|
||||
"CLAUDE_SETTINGS_PATH",
|
||||
"OPENCODE_CONFIG_DIR",
|
||||
] as const
|
||||
|
||||
type EnvKey = (typeof ENV_KEYS)[number]
|
||||
type EnvSnapshot = Record<EnvKey, string | undefined>
|
||||
|
||||
function writePluginFixture(baseDir: string): void {
|
||||
const claudeConfigDir = join(baseDir, "claude-config")
|
||||
const pluginsHome = join(claudeConfigDir, "plugins")
|
||||
const settingsPath = join(claudeConfigDir, "settings.json")
|
||||
const opencodeConfigDir = join(baseDir, "opencode-config")
|
||||
const pluginInstallPath = join(baseDir, "installed-plugins", "daplug")
|
||||
const pluginKey = "daplug@1.0.0"
|
||||
|
||||
mkdirSync(join(pluginInstallPath, ".claude-plugin"), { recursive: true })
|
||||
mkdirSync(join(pluginInstallPath, "commands"), { recursive: true })
|
||||
|
||||
writeFileSync(
|
||||
join(pluginInstallPath, ".claude-plugin", "plugin.json"),
|
||||
JSON.stringify({ name: "daplug", version: "1.0.0" }, null, 2),
|
||||
)
|
||||
writeFileSync(
|
||||
join(pluginInstallPath, "commands", "run-prompt.md"),
|
||||
`---
|
||||
description: Run prompt from daplug
|
||||
---
|
||||
Execute daplug prompt flow.
|
||||
`,
|
||||
)
|
||||
writeFileSync(
|
||||
join(pluginInstallPath, "commands", "templated.md"),
|
||||
`---
|
||||
description: Templated prompt from daplug
|
||||
---
|
||||
Echo $ARGUMENTS and \${user_message}.
|
||||
`,
|
||||
)
|
||||
|
||||
mkdirSync(pluginsHome, { recursive: true })
|
||||
writeFileSync(
|
||||
join(pluginsHome, "installed_plugins.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 2,
|
||||
plugins: {
|
||||
[pluginKey]: [
|
||||
{
|
||||
scope: "user",
|
||||
installPath: pluginInstallPath,
|
||||
version: "1.0.0",
|
||||
installedAt: "2026-01-01T00:00:00.000Z",
|
||||
lastUpdated: "2026-01-01T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
|
||||
mkdirSync(claudeConfigDir, { recursive: true })
|
||||
writeFileSync(
|
||||
settingsPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
enabledPlugins: {
|
||||
[pluginKey]: true,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
mkdirSync(opencodeConfigDir, { recursive: true })
|
||||
|
||||
process.env.CLAUDE_CONFIG_DIR = claudeConfigDir
|
||||
process.env.CLAUDE_PLUGINS_HOME = pluginsHome
|
||||
process.env.CLAUDE_SETTINGS_PATH = settingsPath
|
||||
process.env.OPENCODE_CONFIG_DIR = opencodeConfigDir
|
||||
}
|
||||
|
||||
describe("auto-slash command executor plugin dispatch", () => {
|
||||
let tempDir = ""
|
||||
let envSnapshot: EnvSnapshot
|
||||
|
||||
beforeEach(() => {
|
||||
tempDir = mkdtempSync(join(tmpdir(), "omo-executor-plugin-test-"))
|
||||
envSnapshot = {
|
||||
CLAUDE_CONFIG_DIR: process.env.CLAUDE_CONFIG_DIR,
|
||||
CLAUDE_PLUGINS_HOME: process.env.CLAUDE_PLUGINS_HOME,
|
||||
CLAUDE_SETTINGS_PATH: process.env.CLAUDE_SETTINGS_PATH,
|
||||
OPENCODE_CONFIG_DIR: process.env.OPENCODE_CONFIG_DIR,
|
||||
}
|
||||
writePluginFixture(tempDir)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
for (const key of ENV_KEYS) {
|
||||
const previousValue = envSnapshot[key]
|
||||
if (previousValue === undefined) {
|
||||
delete process.env[key]
|
||||
} else {
|
||||
process.env[key] = previousValue
|
||||
}
|
||||
}
|
||||
rmSync(tempDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it("resolves marketplace plugin commands when plugin loading is enabled", async () => {
|
||||
const result = await executeSlashCommand(
|
||||
{
|
||||
command: "daplug:run-prompt",
|
||||
args: "ship it",
|
||||
raw: "/daplug:run-prompt ship it",
|
||||
},
|
||||
{
|
||||
skills: [],
|
||||
pluginsEnabled: true,
|
||||
},
|
||||
)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.replacementText).toContain("# /daplug:run-prompt Command")
|
||||
expect(result.replacementText).toContain("**Scope**: plugin")
|
||||
})
|
||||
|
||||
it("excludes marketplace commands when plugins are disabled via config toggle", async () => {
|
||||
const result = await executeSlashCommand(
|
||||
{
|
||||
command: "daplug:run-prompt",
|
||||
args: "",
|
||||
raw: "/daplug:run-prompt",
|
||||
},
|
||||
{
|
||||
skills: [],
|
||||
pluginsEnabled: false,
|
||||
},
|
||||
)
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.error).toBe(
|
||||
'Command "/daplug:run-prompt" not found. Use the skill tool to list available skills and commands.',
|
||||
)
|
||||
})
|
||||
|
||||
it("returns standard not-found for unknown namespaced commands", async () => {
|
||||
const result = await executeSlashCommand(
|
||||
{
|
||||
command: "daplug:missing",
|
||||
args: "",
|
||||
raw: "/daplug:missing",
|
||||
},
|
||||
{
|
||||
skills: [],
|
||||
pluginsEnabled: true,
|
||||
},
|
||||
)
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.error).toBe(
|
||||
'Command "/daplug:missing" not found. Use the skill tool to list available skills and commands.',
|
||||
)
|
||||
expect(result.error).not.toContain("Marketplace plugin commands")
|
||||
})
|
||||
|
||||
it("replaces $ARGUMENTS placeholders in plugin command templates", async () => {
|
||||
const result = await executeSlashCommand(
|
||||
{
|
||||
command: "daplug:templated",
|
||||
args: "ship it",
|
||||
raw: "/daplug:templated ship it",
|
||||
},
|
||||
{
|
||||
skills: [],
|
||||
pluginsEnabled: true,
|
||||
},
|
||||
)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.replacementText).toContain("Echo ship it and ship it.")
|
||||
expect(result.replacementText).not.toContain("$ARGUMENTS")
|
||||
expect(result.replacementText).not.toContain("${user_message}")
|
||||
})
|
||||
})
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
sanitizeModelField,
|
||||
getClaudeConfigDir,
|
||||
getOpenCodeConfigDir,
|
||||
discoverPluginCommandDefinitions,
|
||||
} from "../../shared"
|
||||
import { loadBuiltinCommands } from "../../features/builtin-commands"
|
||||
import type { CommandFrontmatter } from "../../features/claude-code-command-loader/types"
|
||||
@@ -15,7 +16,7 @@ import { discoverAllSkills, type LoadedSkill, type LazyContentLoader } from "../
|
||||
import type { ParsedSlashCommand } from "./types"
|
||||
|
||||
interface CommandScope {
|
||||
type: "user" | "project" | "opencode" | "opencode-project" | "skill" | "builtin"
|
||||
type: "user" | "project" | "opencode" | "opencode-project" | "skill" | "builtin" | "plugin"
|
||||
}
|
||||
|
||||
interface CommandMetadata {
|
||||
@@ -99,6 +100,25 @@ function skillToCommandInfo(skill: LoadedSkill): CommandInfo {
|
||||
|
||||
export interface ExecutorOptions {
|
||||
skills?: LoadedSkill[]
|
||||
pluginsEnabled?: boolean
|
||||
enabledPluginsOverride?: Record<string, boolean>
|
||||
}
|
||||
|
||||
function discoverPluginCommands(options?: ExecutorOptions): CommandInfo[] {
|
||||
const pluginDefinitions = discoverPluginCommandDefinitions(options)
|
||||
|
||||
return Object.entries(pluginDefinitions).map(([name, definition]) => ({
|
||||
name,
|
||||
metadata: {
|
||||
name,
|
||||
description: definition.description || "",
|
||||
model: definition.model,
|
||||
agent: definition.agent,
|
||||
subtask: definition.subtask,
|
||||
},
|
||||
content: definition.template,
|
||||
scope: "plugin",
|
||||
}))
|
||||
}
|
||||
|
||||
async function discoverAllCommands(options?: ExecutorOptions): Promise<CommandInfo[]> {
|
||||
@@ -128,6 +148,7 @@ async function discoverAllCommands(options?: ExecutorOptions): Promise<CommandIn
|
||||
|
||||
const skills = options?.skills ?? await discoverAllSkills()
|
||||
const skillCommands = skills.map(skillToCommandInfo)
|
||||
const pluginCommands = discoverPluginCommands(options)
|
||||
|
||||
return [
|
||||
...builtinCommands,
|
||||
@@ -136,6 +157,7 @@ async function discoverAllCommands(options?: ExecutorOptions): Promise<CommandIn
|
||||
...opencodeGlobalCommands,
|
||||
...userCommands,
|
||||
...skillCommands,
|
||||
...pluginCommands,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -179,7 +201,11 @@ async function formatCommandTemplate(cmd: CommandInfo, args: string): Promise<st
|
||||
const commandDir = cmd.path ? dirname(cmd.path) : process.cwd()
|
||||
const withFileRefs = await resolveFileReferencesInText(content, commandDir)
|
||||
const resolvedContent = await resolveCommandsInText(withFileRefs)
|
||||
sections.push(resolvedContent.trim())
|
||||
const resolvedArguments = args
|
||||
const substitutedContent = resolvedContent
|
||||
.replace(/\$\{user_message\}/g, resolvedArguments)
|
||||
.replace(/\$ARGUMENTS/g, resolvedArguments)
|
||||
sections.push(substitutedContent.trim())
|
||||
|
||||
if (args) {
|
||||
sections.push("\n\n---\n")
|
||||
@@ -202,9 +228,7 @@ export async function executeSlashCommand(parsed: ParsedSlashCommand, options?:
|
||||
if (!command) {
|
||||
return {
|
||||
success: false,
|
||||
error: parsed.command.includes(":")
|
||||
? `Marketplace plugin commands like "/${parsed.command}" are not supported. Use .claude/commands/ for custom commands.`
|
||||
: `Command "/${parsed.command}" not found. Use the skill tool to list available skills and commands.`,
|
||||
error: `Command "/${parsed.command}" not found. Use the skill tool to list available skills and commands.`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,15 @@ const sessionProcessedCommandExecutions = new Set<string>()
|
||||
|
||||
export interface AutoSlashCommandHookOptions {
|
||||
skills?: LoadedSkill[]
|
||||
pluginsEnabled?: boolean
|
||||
enabledPluginsOverride?: Record<string, boolean>
|
||||
}
|
||||
|
||||
export function createAutoSlashCommandHook(options?: AutoSlashCommandHookOptions) {
|
||||
const executorOptions: ExecutorOptions = {
|
||||
skills: options?.skills,
|
||||
pluginsEnabled: options?.pluginsEnabled,
|
||||
enabledPluginsOverride: options?.enabledPluginsOverride,
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/claude-code-hooks/ — Claude Code Compatibility
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/keyword-detector/ — Mode Keyword Injection
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,14 +1,53 @@
|
||||
import { beforeEach, describe, expect, test } from "bun:test"
|
||||
declare const require: (name: string) => any
|
||||
const { beforeEach, describe, expect, mock, test } = require("bun:test")
|
||||
|
||||
const readConnectedProvidersCacheMock = mock(() => null)
|
||||
const readProviderModelsCacheMock = mock(() => null)
|
||||
const transformModelForProviderMock = mock((provider: string, model: string) => {
|
||||
if (provider === "github-copilot") {
|
||||
return model
|
||||
.replace("claude-opus-4-6", "claude-opus-4.6")
|
||||
.replace("claude-sonnet-4-6", "claude-sonnet-4.6")
|
||||
.replace("claude-sonnet-4-5", "claude-sonnet-4.5")
|
||||
.replace("claude-haiku-4-5", "claude-haiku-4.5")
|
||||
.replace("claude-sonnet-4", "claude-sonnet-4")
|
||||
.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview")
|
||||
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")
|
||||
}
|
||||
if (provider === "google") {
|
||||
return model
|
||||
.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview")
|
||||
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")
|
||||
}
|
||||
return model
|
||||
})
|
||||
|
||||
mock.module("../../shared/connected-providers-cache", () => ({
|
||||
readConnectedProvidersCache: readConnectedProvidersCacheMock,
|
||||
readProviderModelsCache: readProviderModelsCacheMock,
|
||||
}))
|
||||
|
||||
mock.module("../../shared/provider-model-id-transform", () => ({
|
||||
transformModelForProvider: transformModelForProviderMock,
|
||||
}))
|
||||
|
||||
import {
|
||||
clearPendingModelFallback,
|
||||
createModelFallbackHook,
|
||||
setSessionFallbackChain,
|
||||
setPendingModelFallback,
|
||||
} from "./hook"
|
||||
|
||||
describe("model fallback hook", () => {
|
||||
beforeEach(() => {
|
||||
readConnectedProvidersCacheMock.mockReturnValue(null)
|
||||
readProviderModelsCacheMock.mockReturnValue(null)
|
||||
readConnectedProvidersCacheMock.mockClear()
|
||||
readProviderModelsCacheMock.mockClear()
|
||||
|
||||
clearPendingModelFallback("ses_model_fallback_main")
|
||||
clearPendingModelFallback("ses_model_fallback_ghcp")
|
||||
clearPendingModelFallback("ses_model_fallback_google")
|
||||
})
|
||||
|
||||
test("applies pending fallback on chat.message by overriding model", async () => {
|
||||
@@ -95,8 +134,8 @@ describe("model fallback hook", () => {
|
||||
|
||||
//#then - chain should progress to entry[1], not repeat entry[0]
|
||||
expect(secondOutput.message["model"]).toEqual({
|
||||
providerID: "opencode",
|
||||
modelID: "kimi-k2.5-free",
|
||||
providerID: "zai-coding-plan",
|
||||
modelID: "glm-5",
|
||||
})
|
||||
expect(secondOutput.message["variant"]).toBeUndefined()
|
||||
})
|
||||
@@ -138,4 +177,92 @@ describe("model fallback hook", () => {
|
||||
expect(toastCalls.length).toBe(1)
|
||||
expect(toastCalls[0]?.title).toBe("Model fallback")
|
||||
})
|
||||
|
||||
test("transforms model names for github-copilot provider via fallback chain", async () => {
|
||||
//#given
|
||||
const sessionID = "ses_model_fallback_ghcp"
|
||||
clearPendingModelFallback(sessionID)
|
||||
|
||||
const hook = createModelFallbackHook() as unknown as {
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
// Set a custom fallback chain that routes through github-copilot
|
||||
setSessionFallbackChain(sessionID, [
|
||||
{ providers: ["github-copilot"], model: "claude-sonnet-4-6" },
|
||||
])
|
||||
|
||||
const set = setPendingModelFallback(
|
||||
sessionID,
|
||||
"Atlas (Plan Executor)",
|
||||
"github-copilot",
|
||||
"claude-sonnet-4-6",
|
||||
)
|
||||
expect(set).toBe(true)
|
||||
|
||||
const output = {
|
||||
message: {
|
||||
model: { providerID: "github-copilot", modelID: "claude-sonnet-4-6" },
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
}
|
||||
|
||||
//#when
|
||||
await hook["chat.message"]?.({ sessionID }, output)
|
||||
|
||||
//#then — model name should be transformed from hyphen to dot notation
|
||||
expect(output.message["model"]).toEqual({
|
||||
providerID: "github-copilot",
|
||||
modelID: "claude-sonnet-4.6",
|
||||
})
|
||||
|
||||
clearPendingModelFallback(sessionID)
|
||||
})
|
||||
|
||||
test("transforms model names for google provider via fallback chain", async () => {
|
||||
//#given
|
||||
const sessionID = "ses_model_fallback_google"
|
||||
clearPendingModelFallback(sessionID)
|
||||
|
||||
const hook = createModelFallbackHook() as unknown as {
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
// Set a custom fallback chain that routes through google
|
||||
setSessionFallbackChain(sessionID, [
|
||||
{ providers: ["google"], model: "gemini-3-pro" },
|
||||
])
|
||||
|
||||
const set = setPendingModelFallback(
|
||||
sessionID,
|
||||
"Oracle",
|
||||
"google",
|
||||
"gemini-3-pro",
|
||||
)
|
||||
expect(set).toBe(true)
|
||||
|
||||
const output = {
|
||||
message: {
|
||||
model: { providerID: "google", modelID: "gemini-3-pro" },
|
||||
},
|
||||
parts: [{ type: "text", text: "continue" }],
|
||||
}
|
||||
|
||||
//#when
|
||||
await hook["chat.message"]?.({ sessionID }, output)
|
||||
|
||||
//#then — model name should remain gemini-3-pro because no google transform exists for this ID
|
||||
expect(output.message["model"]).toEqual({
|
||||
providerID: "google",
|
||||
modelID: "gemini-3-pro",
|
||||
})
|
||||
|
||||
clearPendingModelFallback(sessionID)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
||||
import { readConnectedProvidersCache, readProviderModelsCache } from "../../shared/connected-providers-cache"
|
||||
import { selectFallbackProvider } from "../../shared/model-error-classifier"
|
||||
import { transformModelForProvider } from "../../shared/provider-model-id-transform"
|
||||
import { log } from "../../shared/logger"
|
||||
import { getTaskToastManager } from "../../features/task-toast-manager"
|
||||
import type { ChatMessageInput, ChatMessageHandlerOutput } from "../../plugin/chat-message"
|
||||
@@ -145,7 +146,7 @@ export function getNextFallback(
|
||||
|
||||
return {
|
||||
providerID,
|
||||
modelID: fallback.model,
|
||||
modelID: transformModelForProvider(providerID, fallback.model),
|
||||
variant: fallback.variant,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,4 +414,157 @@ describe("preemptive-compaction", () => {
|
||||
restoreTimeouts()
|
||||
}
|
||||
})
|
||||
|
||||
// #given first compaction succeeded and context grew again
|
||||
// #when tool.execute.after runs after new high-token message
|
||||
// #then should trigger compaction again (re-compaction)
|
||||
it("should allow re-compaction when context grows after successful compaction", async () => {
|
||||
const hook = createPreemptiveCompactionHook(ctx as never, {} as never)
|
||||
const sessionID = "ses_recompact"
|
||||
|
||||
// given - first compaction cycle
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-sonnet-4-6",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 170000,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_1" },
|
||||
{ title: "", output: "test", metadata: null }
|
||||
)
|
||||
|
||||
expect(ctx.client.session.summarize).toHaveBeenCalledTimes(1)
|
||||
|
||||
// when - new message with high tokens (context grew after compaction)
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-sonnet-4-6",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 170000,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_2" },
|
||||
{ title: "", output: "test", metadata: null }
|
||||
)
|
||||
|
||||
// then - summarize should fire again
|
||||
expect(ctx.client.session.summarize).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
// #given modelContextLimitsCache has model-specific limit (256k)
|
||||
// #when tokens are above default 78% of 200k but below 78% of 256k
|
||||
// #then should NOT trigger compaction
|
||||
it("should use model-specific context limit from modelContextLimitsCache", async () => {
|
||||
const modelContextLimitsCache = new Map<string, number>()
|
||||
modelContextLimitsCache.set("opencode/kimi-k2.5-free", 262144)
|
||||
|
||||
const hook = createPreemptiveCompactionHook(ctx as never, {} as never, {
|
||||
anthropicContext1MEnabled: false,
|
||||
modelContextLimitsCache,
|
||||
})
|
||||
const sessionID = "ses_kimi_limit"
|
||||
|
||||
// 180k total tokens — above 78% of 200k (156k) but below 78% of 256k (204k)
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "opencode",
|
||||
modelID: "kimi-k2.5-free",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 170000,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_1" },
|
||||
{ title: "", output: "test", metadata: null }
|
||||
)
|
||||
|
||||
expect(ctx.client.session.summarize).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
// #given modelContextLimitsCache has model-specific limit (256k)
|
||||
// #when tokens exceed 78% of model-specific limit
|
||||
// #then should trigger compaction
|
||||
it("should trigger compaction at model-specific threshold", async () => {
|
||||
const modelContextLimitsCache = new Map<string, number>()
|
||||
modelContextLimitsCache.set("opencode/kimi-k2.5-free", 262144)
|
||||
|
||||
const hook = createPreemptiveCompactionHook(ctx as never, {} as never, {
|
||||
anthropicContext1MEnabled: false,
|
||||
modelContextLimitsCache,
|
||||
})
|
||||
const sessionID = "ses_kimi_trigger"
|
||||
|
||||
// 210k total — above 78% of 256k (≈204k)
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "opencode",
|
||||
modelID: "kimi-k2.5-free",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 200000,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_1" },
|
||||
{ title: "", output: "test", metadata: null }
|
||||
)
|
||||
|
||||
expect(ctx.client.session.summarize).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ const PREEMPTIVE_COMPACTION_TIMEOUT_MS = 120_000
|
||||
|
||||
type ModelCacheStateLike = {
|
||||
anthropicContext1MEnabled: boolean
|
||||
modelContextLimitsCache?: Map<string, number>
|
||||
}
|
||||
|
||||
function getAnthropicActualLimit(modelCacheState?: ModelCacheStateLike): number {
|
||||
@@ -91,10 +92,12 @@ export function createPreemptiveCompactionHook(
|
||||
const cached = tokenCache.get(sessionID)
|
||||
if (!cached) return
|
||||
|
||||
const actualLimit =
|
||||
isAnthropicProvider(cached.providerID)
|
||||
? getAnthropicActualLimit(modelCacheState)
|
||||
: DEFAULT_ACTUAL_LIMIT
|
||||
const modelSpecificLimit = !isAnthropicProvider(cached.providerID)
|
||||
? modelCacheState?.modelContextLimitsCache?.get(`${cached.providerID}/${cached.modelID}`)
|
||||
: undefined
|
||||
const actualLimit = isAnthropicProvider(cached.providerID)
|
||||
? getAnthropicActualLimit(modelCacheState)
|
||||
: modelSpecificLimit ?? DEFAULT_ACTUAL_LIMIT
|
||||
|
||||
const lastTokens = cached.tokens
|
||||
const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0)
|
||||
@@ -164,6 +167,7 @@ export function createPreemptiveCompactionHook(
|
||||
modelID: info.modelID ?? "",
|
||||
tokens: info.tokens,
|
||||
})
|
||||
compactedSessions.delete(info.sessionID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/ralph-loop/ — Self-Referential Dev Loop
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/rules-injector/ — Conditional Rules Injection
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getAfplayPath,
|
||||
getPaplayPath,
|
||||
getAplayPath,
|
||||
getTerminalNotifierPath,
|
||||
} from "./session-notification-utils"
|
||||
import { buildWindowsToastScript, escapeAppleScriptText, escapePowerShellSingleQuotedText } from "./session-notification-formatting"
|
||||
|
||||
@@ -39,6 +40,22 @@ export async function sendSessionNotification(
|
||||
): Promise<void> {
|
||||
switch (platform) {
|
||||
case "darwin": {
|
||||
// Try terminal-notifier first — deterministic click-to-focus
|
||||
const terminalNotifierPath = await getTerminalNotifierPath()
|
||||
if (terminalNotifierPath) {
|
||||
const bundleId = process.env.__CFBundleIdentifier
|
||||
try {
|
||||
if (bundleId) {
|
||||
await ctx.$`${terminalNotifierPath} -title ${title} -message ${message} -activate ${bundleId}`
|
||||
} else {
|
||||
await ctx.$`${terminalNotifierPath} -title ${title} -message ${message}`
|
||||
}
|
||||
break
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: osascript (click may open Finder instead of terminal)
|
||||
const osascriptPath = await getOsascriptPath()
|
||||
if (!osascriptPath) return
|
||||
|
||||
|
||||
@@ -32,11 +32,13 @@ export const getPowershellPath = createCommandFinder("powershell")
|
||||
export const getAfplayPath = createCommandFinder("afplay")
|
||||
export const getPaplayPath = createCommandFinder("paplay")
|
||||
export const getAplayPath = createCommandFinder("aplay")
|
||||
export const getTerminalNotifierPath = createCommandFinder("terminal-notifier")
|
||||
|
||||
export function startBackgroundCheck(platform: Platform): void {
|
||||
if (platform === "darwin") {
|
||||
getOsascriptPath().catch(() => {})
|
||||
getAfplayPath().catch(() => {})
|
||||
getTerminalNotifierPath().catch(() => {})
|
||||
} else if (platform === "linux") {
|
||||
getNotifySendPath().catch(() => {})
|
||||
getPaplayPath().catch(() => {})
|
||||
|
||||
@@ -365,4 +365,148 @@ describe("session-notification", () => {
|
||||
// then - only one notification should be sent
|
||||
expect(notificationCalls).toHaveLength(1)
|
||||
})
|
||||
|
||||
function createSenderMockCtx() {
|
||||
const notifyCalls: string[] = []
|
||||
const mockCtx = {
|
||||
$: async (cmd: TemplateStringsArray | string, ...values: any[]) => {
|
||||
const cmdStr = typeof cmd === "string"
|
||||
? cmd
|
||||
: cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
notifyCalls.push(cmdStr)
|
||||
return { stdout: "", stderr: "", exitCode: 0 }
|
||||
},
|
||||
} as any
|
||||
return { mockCtx, notifyCalls }
|
||||
}
|
||||
|
||||
test("should use terminal-notifier with -activate when available on darwin", async () => {
|
||||
// given - terminal-notifier is available and __CFBundleIdentifier is set
|
||||
spyOn(sender, "sendSessionNotification").mockRestore()
|
||||
const { mockCtx, notifyCalls } = createSenderMockCtx()
|
||||
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
|
||||
const originalEnv = process.env.__CFBundleIdentifier
|
||||
process.env.__CFBundleIdentifier = "com.mitchellh.ghostty"
|
||||
|
||||
try {
|
||||
// when - sendSessionNotification is called directly on darwin
|
||||
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
|
||||
|
||||
// then - notification uses terminal-notifier with -activate flag
|
||||
expect(notifyCalls.length).toBeGreaterThanOrEqual(1)
|
||||
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
|
||||
expect(tnCall).toBeDefined()
|
||||
expect(tnCall).toContain("-activate")
|
||||
expect(tnCall).toContain("com.mitchellh.ghostty")
|
||||
} finally {
|
||||
if (originalEnv !== undefined) {
|
||||
process.env.__CFBundleIdentifier = originalEnv
|
||||
} else {
|
||||
delete process.env.__CFBundleIdentifier
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("should fall back to osascript when terminal-notifier is not available", async () => {
|
||||
// given - terminal-notifier is NOT available
|
||||
spyOn(sender, "sendSessionNotification").mockRestore()
|
||||
const { mockCtx, notifyCalls } = createSenderMockCtx()
|
||||
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue(null)
|
||||
spyOn(utils, "getOsascriptPath").mockResolvedValue("/usr/bin/osascript")
|
||||
|
||||
// when - sendSessionNotification is called directly on darwin
|
||||
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
|
||||
|
||||
// then - notification uses osascript (fallback)
|
||||
expect(notifyCalls.length).toBeGreaterThanOrEqual(1)
|
||||
const osascriptCall = notifyCalls.find(c => c.includes("osascript"))
|
||||
expect(osascriptCall).toBeDefined()
|
||||
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
|
||||
expect(tnCall).toBeUndefined()
|
||||
})
|
||||
|
||||
test("should fall back to osascript when terminal-notifier execution fails", async () => {
|
||||
// given - terminal-notifier exists but invocation fails
|
||||
spyOn(sender, "sendSessionNotification").mockRestore()
|
||||
const notifyCalls: string[] = []
|
||||
const mockCtx = {
|
||||
$: async (cmd: TemplateStringsArray | string, ...values: unknown[]) => {
|
||||
const cmdStr = typeof cmd === "string"
|
||||
? cmd
|
||||
: cmd.reduce((acc, part, index) => `${acc}${part}${String(values[index] ?? "")}`, "")
|
||||
notifyCalls.push(cmdStr)
|
||||
|
||||
if (cmdStr.includes("terminal-notifier")) {
|
||||
throw new Error("terminal-notifier failed")
|
||||
}
|
||||
|
||||
return { stdout: "", stderr: "", exitCode: 0 }
|
||||
},
|
||||
} as any
|
||||
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
|
||||
spyOn(utils, "getOsascriptPath").mockResolvedValue("/usr/bin/osascript")
|
||||
|
||||
// when - sendSessionNotification is called directly on darwin
|
||||
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
|
||||
|
||||
// then - osascript fallback should be attempted after terminal-notifier failure
|
||||
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
|
||||
const osascriptCall = notifyCalls.find(c => c.includes("osascript"))
|
||||
expect(tnCall).toBeDefined()
|
||||
expect(osascriptCall).toBeDefined()
|
||||
})
|
||||
|
||||
test("should invoke terminal-notifier without array interpolation", async () => {
|
||||
// given - shell interpolation rejects array values
|
||||
spyOn(sender, "sendSessionNotification").mockRestore()
|
||||
const notifyCalls: string[] = []
|
||||
const mockCtx = {
|
||||
$: async (cmd: TemplateStringsArray | string, ...values: unknown[]) => {
|
||||
if (values.some(Array.isArray)) {
|
||||
throw new Error("array interpolation unsupported")
|
||||
}
|
||||
|
||||
const commandString = typeof cmd === "string"
|
||||
? cmd
|
||||
: cmd.reduce((acc, part, index) => `${acc}${part}${String(values[index] ?? "")}`, "")
|
||||
notifyCalls.push(commandString)
|
||||
return { stdout: "", stderr: "", exitCode: 0 }
|
||||
},
|
||||
} as any
|
||||
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
|
||||
spyOn(utils, "getOsascriptPath").mockResolvedValue("/usr/bin/osascript")
|
||||
|
||||
// when - terminal-notifier command is executed
|
||||
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
|
||||
|
||||
// then - terminal-notifier succeeds directly and fallback is not used
|
||||
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
|
||||
const osascriptCall = notifyCalls.find(c => c.includes("osascript"))
|
||||
expect(tnCall).toBeDefined()
|
||||
expect(osascriptCall).toBeUndefined()
|
||||
})
|
||||
|
||||
test("should use terminal-notifier without -activate when __CFBundleIdentifier is not set", async () => {
|
||||
// given - terminal-notifier available but no bundle ID
|
||||
spyOn(sender, "sendSessionNotification").mockRestore()
|
||||
const { mockCtx, notifyCalls } = createSenderMockCtx()
|
||||
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
|
||||
const originalEnv = process.env.__CFBundleIdentifier
|
||||
delete process.env.__CFBundleIdentifier
|
||||
|
||||
try {
|
||||
// when - sendSessionNotification is called directly on darwin
|
||||
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
|
||||
|
||||
// then - terminal-notifier used but without -activate flag
|
||||
expect(notifyCalls.length).toBeGreaterThanOrEqual(1)
|
||||
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
|
||||
expect(tnCall).toBeDefined()
|
||||
expect(tnCall).not.toContain("-activate")
|
||||
} finally {
|
||||
if (originalEnv !== undefined) {
|
||||
process.env.__CFBundleIdentifier = originalEnv
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/session-recovery/ — Auto Session Error Recovery
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
* inconsistencies defensively while maintaining backwards compatibility.
|
||||
*/
|
||||
|
||||
import { normalizeModelID } from "../../shared"
|
||||
|
||||
/**
|
||||
* Extracts provider-specific prefix from model ID (if present).
|
||||
* Custom providers may use prefixes for routing (e.g., vertex_ai/, openai/).
|
||||
@@ -36,24 +38,6 @@ function extractModelPrefix(modelID: string): { prefix: string; base: string } {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes model IDs to use consistent hyphen formatting.
|
||||
* GitHub Copilot may use dots (claude-opus-4.6) but our maps use hyphens (claude-opus-4-6).
|
||||
* This ensures lookups work regardless of format.
|
||||
*
|
||||
* @example
|
||||
* normalizeModelID("claude-opus-4.6") // "claude-opus-4-6"
|
||||
* normalizeModelID("gemini-3.5-pro") // "gemini-3-5-pro"
|
||||
* normalizeModelID("gpt-5.2") // "gpt-5-2"
|
||||
* normalizeModelID("vertex_ai/claude-opus-4.6") // "vertex_ai/claude-opus-4-6"
|
||||
*/
|
||||
function normalizeModelID(modelID: string): string {
|
||||
// Replace dots with hyphens when followed by a digit
|
||||
// This handles version numbers like 4.5 → 4-5, 5.2 → 5-2
|
||||
return modelID.replace(/\.(\d+)/g, "-$1")
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Maps model IDs to their "high reasoning" variant (internal convention)
|
||||
// For OpenAI models, this signals that reasoning_effort should be set to "high"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/hooks/todo-continuation-enforcer/ — Boulder Continuation Mechanism
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { initConfigContext } from "./cli/config-manager/config-context"
|
||||
import type { Plugin } from "@opencode-ai/plugin"
|
||||
|
||||
import type { HookName } from "./config"
|
||||
@@ -14,6 +15,8 @@ import { injectServerAuthIntoClient, log } from "./shared"
|
||||
import { startTmuxCheck } from "./tools"
|
||||
|
||||
const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
// Initialize config context for plugin runtime (prevents warnings from hooks)
|
||||
initConfigContext("opencode", null)
|
||||
log("[OhMyOpenCodePlugin] ENTRY - plugin loading", {
|
||||
directory: ctx.directory,
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/mcp/ — 3 Built-in Remote MCPs
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/plugin-handlers/ — 6-Phase Config Loading Pipeline
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -99,9 +99,9 @@ export function applyToolConfig(params: {
|
||||
}
|
||||
|
||||
params.config.permission = {
|
||||
...(params.config.permission as Record<string, unknown>),
|
||||
webfetch: "allow",
|
||||
external_directory: "allow",
|
||||
...(params.config.permission as Record<string, unknown>),
|
||||
task: "deny",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# src/plugin/ — 8 OpenCode Hook Handlers + Hook Composition
|
||||
|
||||
**Generated:** 2026-02-24
|
||||
**Generated:** 2026-03-02
|
||||
|
||||
## OVERVIEW
|
||||
|
||||
|
||||
@@ -106,4 +106,41 @@ describe("createChatHeadersHandler", () => {
|
||||
|
||||
expect(output.headers["x-initiator"]).toBeUndefined()
|
||||
})
|
||||
|
||||
test("skips x-initiator override when model uses @ai-sdk/github-copilot", async () => {
|
||||
const handler = createChatHeadersHandler({
|
||||
ctx: {
|
||||
client: {
|
||||
session: {
|
||||
message: async () => ({
|
||||
data: {
|
||||
parts: [
|
||||
{
|
||||
type: "text",
|
||||
text: `notification\n${OMO_INTERNAL_INITIATOR_MARKER}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
})
|
||||
const output: { headers: Record<string, string> } = { headers: {} }
|
||||
|
||||
await handler(
|
||||
{
|
||||
sessionID: "ses_4",
|
||||
provider: { id: "github-copilot" },
|
||||
model: { api: { npm: "@ai-sdk/github-copilot" } },
|
||||
message: {
|
||||
id: "msg_4",
|
||||
role: "user",
|
||||
},
|
||||
},
|
||||
output,
|
||||
)
|
||||
|
||||
expect(output.headers["x-initiator"]).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -123,6 +123,17 @@ export function createChatHeadersHandler(args: { ctx: PluginContext }): (input:
|
||||
if (!isChatHeadersOutput(output)) return
|
||||
|
||||
if (!isCopilotProvider(normalizedInput.provider.id)) return
|
||||
|
||||
// Do not override x-initiator when @ai-sdk/github-copilot is active.
|
||||
// OpenCode's copilot fetch wrapper already sets x-initiator based on
|
||||
// the actual request body content. Overriding it here causes a mismatch
|
||||
// that the Copilot API rejects with "invalid initiator".
|
||||
const model = isRecord(input) && isRecord((input as Record<string, unknown>).model)
|
||||
? (input as Record<string, unknown>).model as Record<string, unknown>
|
||||
: undefined
|
||||
const api = model && isRecord(model.api) ? model.api as Record<string, unknown> : undefined
|
||||
if (api?.npm === "@ai-sdk/github-copilot") return
|
||||
|
||||
if (!(await isOmoInternalMessage(normalizedInput, ctx.client))) return
|
||||
|
||||
output.headers["x-initiator"] = "agent"
|
||||
|
||||
@@ -334,8 +334,8 @@ describe("createEventHandler - model fallback", () => {
|
||||
|
||||
//#then - second fallback entry applied (chain advanced)
|
||||
expect(second.message["model"]).toEqual({
|
||||
providerID: "opencode",
|
||||
modelID: "kimi-k2.5-free",
|
||||
providerID: "zai-coding-plan",
|
||||
modelID: "glm-5",
|
||||
})
|
||||
expect(second.message["variant"]).toBeUndefined()
|
||||
expect(abortCalls).toEqual([sessionID, sessionID])
|
||||
|
||||
@@ -111,6 +111,7 @@ export function createContinuationHooks(args: {
|
||||
isContinuationStopped: (sessionID: string) =>
|
||||
stopContinuationGuard?.isStopped(sessionID) ?? false,
|
||||
agentOverrides: pluginConfig.agents,
|
||||
autoCommit: pluginConfig.start_work?.auto_commit,
|
||||
}))
|
||||
: null
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
|
||||
import type { HookName } from "../../config"
|
||||
import type { HookName, OhMyOpenCodeConfig } from "../../config"
|
||||
import type { LoadedSkill } from "../../features/opencode-skill-loader/types"
|
||||
import type { PluginContext } from "../types"
|
||||
|
||||
@@ -13,12 +13,20 @@ export type SkillHooks = {
|
||||
|
||||
export function createSkillHooks(args: {
|
||||
ctx: PluginContext
|
||||
pluginConfig: OhMyOpenCodeConfig
|
||||
isHookEnabled: (hookName: HookName) => boolean
|
||||
safeHookEnabled: boolean
|
||||
mergedSkills: LoadedSkill[]
|
||||
availableSkills: AvailableSkill[]
|
||||
}): SkillHooks {
|
||||
const { ctx, isHookEnabled, safeHookEnabled, mergedSkills, availableSkills } = args
|
||||
const {
|
||||
ctx,
|
||||
pluginConfig,
|
||||
isHookEnabled,
|
||||
safeHookEnabled,
|
||||
mergedSkills,
|
||||
availableSkills,
|
||||
} = args
|
||||
|
||||
const safeHook = <T>(hookName: HookName, factory: () => T): T | null =>
|
||||
safeCreateHook(hookName, factory, { enabled: safeHookEnabled })
|
||||
@@ -30,7 +38,11 @@ export function createSkillHooks(args: {
|
||||
|
||||
const autoSlashCommand = isHookEnabled("auto-slash-command")
|
||||
? safeHook("auto-slash-command", () =>
|
||||
createAutoSlashCommandHook({ skills: mergedSkills }))
|
||||
createAutoSlashCommandHook({
|
||||
skills: mergedSkills,
|
||||
pluginsEnabled: pluginConfig.claude_code?.plugins ?? true,
|
||||
enabledPluginsOverride: pluginConfig.claude_code?.plugins_override,
|
||||
}))
|
||||
: null
|
||||
|
||||
return { categorySkillReminder, autoSlashCommand }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export function createSystemTransformHandler(): (
|
||||
input: { sessionID: string },
|
||||
input: { sessionID?: string; model: { id: string; providerID: string; [key: string]: unknown } },
|
||||
output: { system: string[] },
|
||||
) => Promise<void> {
|
||||
return async (): Promise<void> => {}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user