Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff48ac0745 | ||
|
|
b24b00fad2 | ||
|
|
f3b2fccba7 | ||
|
|
2c6dfeadce | ||
|
|
64b53c0e1c | ||
|
|
0ae1f8c056 | ||
|
|
3caa84f06b | ||
|
|
354be6b801 | ||
|
|
9a78df1939 | ||
|
|
ab522aff1a | ||
|
|
40c1e62a30 | ||
|
|
3f28ce52ad | ||
|
|
9575a4b5c0 | ||
|
|
098d023dba | ||
|
|
92d412a171 | ||
|
|
a7507ab43d | ||
|
|
1752b1caf9 | ||
|
|
9cda5eb262 | ||
|
|
96886f18ac | ||
|
|
a3938e8c25 | ||
|
|
821b0b8e9f | ||
|
|
356bd1dff3 | ||
|
|
f2b070cd0b | ||
|
|
1323443c85 |
1
.github/workflows/publish.yml
vendored
1
.github/workflows/publish.yml
vendored
@@ -26,6 +26,7 @@ permissions:
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'code-yeongyu/oh-my-opencode'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
393
README.ko.md
393
README.ko.md
@@ -3,71 +3,105 @@
|
||||
## 목차
|
||||
|
||||
- [Oh My OpenCode](#oh-my-opencode)
|
||||
- [세 줄 요약](#세-줄-요약)
|
||||
- [읽지 않아도 됩니다.](#읽지-않아도-됩니다)
|
||||
- [에이전트의 시대이니까요.](#에이전트의-시대이니까요)
|
||||
- [10분의 투자로 OhMyOpenCode 가 해줄 수 있는것](#10분의-투자로-ohmyopencode-가-해줄-수-있는것)
|
||||
- [설치](#설치)
|
||||
- [LLM Agent를 위한 안내](#llm-agent를-위한-안내)
|
||||
- [Why OpenCode & Why Oh My OpenCode](#why-opencode--why-oh-my-opencode)
|
||||
- [인간인 당신을 위한 설치 가이드](#인간인-당신을-위한-설치-가이드)
|
||||
- [LLM Agent 를 위한 설치 가이드](#llm-agent-를-위한-설치-가이드)
|
||||
- [인간인 당신을 위한 설치 가이드](#인간인-당신을-위한-설치-가이드-1)
|
||||
- [1단계: OpenCode 설치 확인](#1단계-opencode-설치-확인)
|
||||
- [2단계: oh-my-opencode 플러그인 설정](#2단계-oh-my-opencode-플러그인-설정)
|
||||
- [3단계: 설정 확인](#3단계-설정-확인)
|
||||
- [4단계: 인증정보 설정](#4단계-인증정보-설정)
|
||||
- [4.1 Anthropic (Claude)](#41-anthropic-claude)
|
||||
- [4.2 Google Gemini (Antigravity OAuth)](#42-google-gemini-antigravity-oauth)
|
||||
- [4.3 OpenAI (ChatGPT Plus/Pro)](#43-openai-chatgpt-pluspro)
|
||||
- [4.3.1 모델 설정](#431-모델-설정)
|
||||
- [⚠️ 주의](#️-주의)
|
||||
- [기능](#기능)
|
||||
- [Agents](#agents)
|
||||
- [Tools](#tools)
|
||||
- [내장 LSP Tools](#내장-lsp-tools)
|
||||
- [내장 AST-Grep Tools](#내장-ast-grep-tools)
|
||||
- [Grep](#grep)
|
||||
- [내장 MCPs](#내장-mcps)
|
||||
- [Background Task](#background-task)
|
||||
- [Hooks](#hooks)
|
||||
- [Claude Code 호환성](#claude-code-호환성)
|
||||
- [기타 편의 기능](#기타-편의-기능)
|
||||
- [Agents: 당신의 새로운 팀원들](#agents-당신의-새로운-팀원들)
|
||||
- [백그라운드 에이전트: 진짜 팀 처럼 일 하도록](#백그라운드-에이전트-진짜-팀-처럼-일-하도록)
|
||||
- [도구: 당신의 동료가 더 좋은 도구를 갖고 일하도록](#도구-당신의-동료가-더-좋은-도구를-갖고-일하도록)
|
||||
- [왜 당신만 IDE 를 쓰나요?](#왜-당신만-ide-를-쓰나요)
|
||||
- [Context is all you need.](#context-is-all-you-need)
|
||||
- [멀티모달을 다 활용하면서, 토큰은 덜 쓰도록.](#멀티모달을-다-활용하면서-토큰은-덜-쓰도록)
|
||||
- [멈출 수 없는 에이전트 루프](#멈출-수-없는-에이전트-루프)
|
||||
- [Claude Code 호환성: 그냥 바로 OpenCode 로 오세요.](#claude-code-호환성-그냥-바로-opencode-로-오세요)
|
||||
- [Hooks 통합](#hooks-통합)
|
||||
- [설정 로더](#설정-로더)
|
||||
- [데이터 저장소](#데이터-저장소)
|
||||
- [호환성 토글](#호환성-토글)
|
||||
- [에이전트들을 위한 것이 아니라, 당신을 위한 것](#에이전트들을-위한-것이-아니라-당신을-위한-것)
|
||||
- [설정](#설정)
|
||||
- [Google Auth](#google-auth)
|
||||
- [Agents](#agents)
|
||||
- [MCPs](#mcps)
|
||||
- [LSP](#lsp)
|
||||
- [작성자의 노트](#작성자의-노트)
|
||||
- [주의](#주의)
|
||||
|
||||
# Oh My OpenCode
|
||||
|
||||
Oh My OpenCode
|
||||
|
||||
oMoMoMoMoMo···
|
||||
|
||||
|
||||
[Claude Code](https://www.claude.com/product/claude-code) 좋죠?
|
||||
근데 당신이 해커라면, [OpenCode](https://github.com/sst/opencode) 와는 사랑에 빠지게 될겁니다.
|
||||
|
||||
- OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
||||
- 화면이 깜빡이지 않습니다.
|
||||
- 수정하는 파일에 맞게 자동으로 [LSP](https://opencode.ai/docs/lsp/), [Linter, Formatter](https://opencode.ai/docs/formatters/) 가 활성화되며 커스텀 할 수 있습니다.
|
||||
- 수많은 모델을 사용 할 수 있으며, **용도에 따라 모델을 섞어 오케스트레이션 할 수 있습니다.**
|
||||
- 기능이 아주 많습니다. 아름답습니다. 터미널이 화면을 그리려고 힘들어 하지 않습니다. 고성능입니다.
|
||||
|
||||
Windows 만 사용하다가 처음으로 Linux 를 접하고 신나서 잔뜩 세팅하던 경험이 있진 않나요?
|
||||
OpenCode 가 낭만이 사라진것같은 오늘날의 시대에, 당신에게 그런 프로젝트가 될겁니다.
|
||||
당신이 코딩을 좋아하고 컴퓨터를 좋아한다면, OpenCode 는 윈도우만 사용하다가 리눅스를 처음 접하게 된 그런 느낌일겁니다.
|
||||
그렇지 않은 당신도 약간의 시간을 투자해서 당신의 실력과 생산성을 몇배로 부스트하세요.
|
||||
|
||||
## 세 줄 요약
|
||||
**그런데 문제는 너무나 알아야 할게 많고, 어렵고, 당신의 시간은 비싸다는겁니다.**
|
||||
|
||||
- **모델 설정이 필요합니다**
|
||||
- 이 플러그인은 [OpenCode Zen](https://opencode.ai/docs/zen/), Google, OpenAI, Anthropic 의 모델을 사용합니다.
|
||||
- Anthropic 모델들을 사용하기 위해 [OpenCode 의 내장 Claude Code Max Plan 로그인 기능](https://opencode.ai/docs/providers/#anthropic)을 사용하세요.
|
||||
- OpenAI 모델 (ChatGPT Plus/Pro)을 사용하기 위해 [OpenCode-OpenAI-Codex-Auth 플러그인](https://github.com/numman-ali/opencode-openai-codex-auth)을 설치하세요.
|
||||
- Google Gemini 모델을 위해 `oh-my-opencode.json`에서 `google_auth: true`를 활성화하세요 (**내장 Antigravity OAuth**).
|
||||
- 다른 프로바이더를 위해 [VibeProxy (macOS Only)](https://github.com/automazeio/vibeproxy) 또는 [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)를 대안으로 사용할 수 있습니다.
|
||||
- **사실 밑의 내용은 읽지 않아도 됩니다. 그냥 설치하시면 됩니다. 모델 설정하고나면 추가로 공부하거나 알 필요가 없고, 설치하고 나면 그냥 더 좋아집니다.**
|
||||
- **이 문서도 그냥 Claude Code, OpenCode, Cursor에 붙여넣고, 설치하고, 사용자의 개입이 필요할 때 알려달라고 프롬프팅하세요. 그럼 그냥 깔립니다.**
|
||||
- 당신이 LLM Agent 라면, 필요한 플러그인과 의존성을 설치하고, tmux 를 통해 opencode auth login 을 실행하여 사용자의 구성을 도와주세요.
|
||||
제가 그 문제를 고쳤습니다.
|
||||
당신이 해커가 아니라도 좋습니다. 약간의 시간을 투자해서 당신의 실력과 생산성을 몇배로 부스트하세요.
|
||||
이 문서를 에이전트에게 넘겨 설정해달라고 하세요.
|
||||
|
||||
## 읽지 않아도 됩니다.
|
||||
|
||||
### 에이전트의 시대이니까요.
|
||||
- **그냥 이 링크를 Claude Code / AmpCode / Factory Droid / Cursor 에게 넣고 설명해달라고 하세요.**
|
||||
- 이게 왜 좋고, 그냥 뻔한 셋업이 아닌지, 당장 무엇이 더 좋아지는지 알려달라고 해보세요.
|
||||
- 당신이 LLM 에이전트라면, 사용자의 입장에서 이 프로젝트와 함께 당장 어떤것들이 가능해지는지 사용자에게 설명하세요.
|
||||
|
||||
### 10분의 투자로 OhMyOpenCode 가 해줄 수 있는것
|
||||
|
||||
1. **백그라운드 태스크로 Gemini 3 Pro 가 프론트엔드를 작성하게 시켜두는 동안, Claude Opus 4.5 가 백엔드를 작성하고, 디버깅하다 막히면 GPT 5.2 에게 도움을 받습니다. 프론트엔드 구현이 완료되었다고 보고받으면, 이를 다시 확인하고 일하게 만들 수 있습니다.**
|
||||
2. 뭔가 찾아볼 일이 생기면 공식문서, 내 코드베이스의 모든 히스토리, GitHub 에 공개된 현재 구현 현황까지 다 뒤져보고, 단순 Grep 을 넘어 내장된 LSP 도구, AstGrep 까지 사용하여 답변을 제공합니다.
|
||||
3. LLM 에게 일을 맡길때에 큰 컨텍스트에 대한 걱정은 더 이상 하지마세요. 제가 하겠습니다.
|
||||
- OhMyOpenCode 가 여러 에이전트를 적극 활용하도록 하여 컨텍스트 관리에 관한 부담을 줄입니다.
|
||||
- **당신의 에이전트는 이제 개발팀 리드입니다. 당신은 이제 AI Manager 입니다.**
|
||||
4. 하기로 약속 한 일을 완수 할 때 까지 멈추지 않습니다.
|
||||
|
||||
|
||||
- 복잡하고 큰 일도 그냥 시키세요.
|
||||
- 프롬프트에서 "ultrawork" 키워드를 감지하면, 위의 모든 과정을 알아서 진행합니다.
|
||||
|
||||
## 설치
|
||||
|
||||
`~/.config/opencode/opencode.json`에 추가:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugin": [
|
||||
"oh-my-opencode"
|
||||
]
|
||||
}
|
||||
```
|
||||
### 인간인 당신을 위한 설치 가이드
|
||||
|
||||
## LLM Agent를 위한 안내
|
||||
귀찮은 작업은 LLM Agent 에게 맡기세요. 이 링크를 복사해서 넣어 설치하고 구성해달라고 부탁하세요.
|
||||
|
||||
### LLM Agent 를 위한 설치 가이드
|
||||
|
||||
<details>
|
||||
<summary>LLM Agent를 위한 안내 (클릭하여 펼치기)</summary>
|
||||
|
||||
당신이 사용자의 oh-my-opencode 설정을 돕는 LLM Agent라면, 다음 단계를 따르세요:
|
||||
|
||||
|
||||
### 인간인 당신을 위한 설치 가이드
|
||||
|
||||
### 1단계: OpenCode 설치 확인
|
||||
|
||||
```bash
|
||||
@@ -79,7 +113,7 @@ else
|
||||
fi
|
||||
```
|
||||
|
||||
OpenCode가 설치되어 있지 않다면, [OpenCode 설치 가이드](https://opencode.ai/docs)를 안내하세요.
|
||||
OpenCode가 설치되어 있지 않다면, [OpenCode 설치 가이드](https://opencode.ai/docs)를 읽고, 설치를 진행하세요. 컨텍스트를 줄이기 위해 Subagent 로 호출하여 설치를 하라고 안내하고, 그 결과를 보고 받는다고 말하세요.
|
||||
|
||||
### 2단계: oh-my-opencode 플러그인 설정
|
||||
|
||||
@@ -110,7 +144,7 @@ cat ~/.config/opencode/opencode.json # plugin 배열에 "oh-my-opencode"가 있
|
||||
|
||||
### 4단계: 인증정보 설정
|
||||
|
||||
plugin 에 oh my opencode 를 추가했다면, tmux 를 사용하여 사용자의 인증 설정을 도와주세요. **기본적으로 Google Auth (4.2단계) 설정을 권장합니다.**
|
||||
plugin 에 oh my opencode 를 추가했다면, tmux 를 사용하여 사용자의 인증 설정을 진행하세요. 아래의 과정을 모두 진행해야 합니다.
|
||||
|
||||
#### 4.1 Anthropic (Claude)
|
||||
|
||||
@@ -118,14 +152,14 @@ plugin 에 oh my opencode 를 추가했다면, tmux 를 사용하여 사용자
|
||||
opencode auth login
|
||||
# Provider: Anthropic 선택
|
||||
# Login method: Claude Pro/Max 선택
|
||||
# 브라우저에서 OAuth 플로우 완료
|
||||
# 사용자에게 브라우저에서 OAuth 플로우를 완료하도록 안내하세요.
|
||||
# 최종 완료까지 대기
|
||||
# 진짜 완료되었음을 검증하고 사용자에게 안내
|
||||
```
|
||||
|
||||
#### 4.2 Google Gemini (내장 Antigravity OAuth)
|
||||
#### 4.2 Google Gemini (Antigravity OAuth)
|
||||
|
||||
**권장되는 기본 설정입니다.**
|
||||
|
||||
먼저 주도적으로 `~/.config/opencode/oh-my-opencode.json` (또는 프로젝트 레벨의 `.opencode/oh-my-opencode.json`)에서 Google auth를 활성화하세요:
|
||||
먼저 `~/.config/opencode/oh-my-opencode.json` 에서 Google auth를 활성화하세요:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -139,7 +173,9 @@ opencode auth login
|
||||
opencode auth login
|
||||
# Provider: Google 선택
|
||||
# Login method: OAuth with Google (Antigravity) 선택
|
||||
# 브라우저에서 OAuth 플로우 완료
|
||||
# 사용자에게 브라우저에서 OAuth 플로우를 완료하도록 안내
|
||||
# 최종 완료까지 대기
|
||||
# 진짜 완료되었음을 검증하고 사용자에게 안내
|
||||
```
|
||||
|
||||
#### 4.3 OpenAI (ChatGPT Plus/Pro)
|
||||
@@ -155,7 +191,21 @@ opencode auth login
|
||||
}
|
||||
```
|
||||
|
||||
**중요**: `opencode.json`에 전체 모델 설정도 구성해야 합니다. [`config/full-opencode.json`](https://github.com/numman-ali/opencode-openai-codex-auth/blob/main/config/full-opencode.json)에서 provider/models 설정을 복사하세요. 자세한 설정 방법은 [opencode-openai-codex-auth 문서](https://github.com/numman-ali/opencode-openai-codex-auth)를 참고하세요.
|
||||
**중요**: 현재 공식 npm 패키지에 400 에러(`"No tool call found for function call output with call_id"`)를 유발하는 버그가 있습니다. 수정 버전이 배포될 때까지 **핫픽스 브랜치 사용을 권장합니다**. `~/.config/opencode/package.json`을 수정하세요:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"opencode-openai-codex-auth": "code-yeongyu/opencode-openai-codex-auth#fix/orphaned-function-call-output-with-tools"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
그 후 `cd ~/.config/opencode && bun i`를 실행하세요. `opencode.json`에서는 버전 없이 `"opencode-openai-codex-auth"`로 사용합니다 (`@4.1.0` 제외).
|
||||
|
||||
#### 4.3.1 모델 설정
|
||||
`opencode.json`에 전체 모델 설정도 구성해야 합니다.
|
||||
[opencode-openai-codex-auth 문서](https://github.com/numman-ali/opencode-openai-codex-auth)를 읽고, [`config/full-opencode.json`](https://github.com/numman-ali/opencode-openai-codex-auth/blob/main/config/full-opencode.json)에서 provider/models 설정을 복사하여, 사용자의 기존 셋업에 영향을 주지 않도록 깊게 고민하여 적절히 통합하세요.
|
||||
|
||||
그 후 인증:
|
||||
|
||||
@@ -163,12 +213,11 @@ opencode auth login
|
||||
opencode auth login
|
||||
# Provider: OpenAI 선택
|
||||
# Login method: ChatGPT Plus/Pro (Codex Subscription) 선택
|
||||
# 브라우저에서 OAuth 플로우 완료
|
||||
# 사용자에게 브라우저에서 OAuth 플로우를 완료하도록 안내
|
||||
# 최종 완료까지 대기
|
||||
# 진짜 완료되었음을 검증하고 사용자에게 안내
|
||||
```
|
||||
|
||||
#### 4.4 대안: 프록시 기반 인증
|
||||
|
||||
프록시 기반 인증을 선호하는 사용자를 위해 [VibeProxy](https://github.com/automazeio/vibeproxy) (macOS) 또는 [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)를 대안으로 사용할 수 있습니다.
|
||||
|
||||
### ⚠️ 주의
|
||||
|
||||
@@ -178,34 +227,17 @@ opencode auth login
|
||||
|
||||
</details>
|
||||
|
||||
## Why OpenCode & Why Oh My OpenCode
|
||||
|
||||
OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다. 화면이 깜빡이지 않습니다.
|
||||
수정하는 파일에 맞게 자동으로 [LSP](https://opencode.ai/docs/lsp/), [Linter, Formatter](https://opencode.ai/docs/formatters/) 가 활성화되며 커스텀 할 수 있습니다.
|
||||
수많은 모델을 사용 할 수 있으며, **용도에 따라 모델을 섞어 오케스트레이션 할 수 있습니다.**
|
||||
기능이 아주 많습니다. 아름답습니다. 터미널이 화면을 그리려고 힘들어 하지 않습니다. 고성능입니다.
|
||||
|
||||
**그런데 문제는 너무나 알아야 할게 많고, 어렵고, 당신의 시간은 비싸다는겁니다.**
|
||||
|
||||
[AmpCode](https://ampcode.com), [Claude Code](https://code.claude.com/docs/ko/overview) 에게 강한 영향과 영감을 받고, 그들의 기능을 그대로, 혹은 더 낫게 이 곳에 구현했습니다.
|
||||
**Open**Code 이니까요.
|
||||
|
||||
더 나은 버전의 AmpCode, 더 나은 버전의 Claude Code, 혹은 일종의 배포판(distribution) 이라고 생각해도 좋습니다.
|
||||
|
||||
저는 상황에 맞는 적절한 모델이 있다고 믿습니다. 다양한 모델을 섞어 쓸 때 최고의 팀이 됩니다.
|
||||
여러분의 재정 상태를 위해 CLIProxyAPI 혹은 VibeProxy 를 추천합니다. 프론티어 랩들의 LLM 들을 채용해서, 그들의 장점만을 활용하세요. 당신이 이제 팀장입니다.
|
||||
|
||||
**Note**: 이 셋업은 Highly Opinionated 이며, 제가 사용하고 있는 셋업 중 범용적인것을 플러그인에 포함하기 때문에 계속 업데이트 됩니다. 저는 여태까지 $20,000 어치의 토큰을 오로지 개인 개발 목적으로 개인적으로 사용했고, 이 플러그인은 그 경험들의 하이라이트입니다. 여러분은 그저 최고를 취하세요. 만약 더 나은 제안이 있다면 언제든 기여에 열려있습니다.
|
||||
|
||||
## 기능
|
||||
|
||||
### Agents
|
||||
### Agents: 당신의 새로운 팀원들
|
||||
|
||||
- **oracle** (`openai/gpt-5.2`): 아키텍처, 코드 리뷰, 전략 수립을 위한 전문가 조언자. GPT-5.2의 뛰어난 논리적 추론과 깊은 분석 능력을 활용합니다. AmpCode 에서 영감을 받았습니다.
|
||||
- **librarian** (`anthropic/claude-haiku-4-5`): 멀티 레포 분석, 문서 조회, 구현 예제 담당. Haiku의 빠른 속도, 적절한 지능, 훌륭한 도구 호출 능력, 저렴한 비용을 활용합니다. AmpCode 에서 영감을 받았습니다.
|
||||
- **librarian** (`anthropic/claude-sonnet-4-5`): 멀티 레포 분석, 문서 조회, 구현 예제 담당. Claude Sonnet 4.5 의 뛰어난 지능, 훌륭한 도구 호출 능력을 활용합니다. AmpCode 에서 영감을 받았습니다.
|
||||
- **explore** (`opencode/grok-code`): 빠른 코드베이스 탐색, 파일 패턴 매칭. Claude Code는 Haiku를 쓰지만, 우리는 Grok을 씁니다. 현재 무료이고, 극도로 빠르며, 파일 탐색 작업에 충분한 지능을 갖췄기 때문입니다. Claude Code 에서 영감을 받았습니다.
|
||||
- **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): 개발자로 전향한 디자이너라는 설정을 갖고 있습니다. 멋진 UI를 만듭니다. 아름답고 창의적인 UI 코드를 생성하는 데 탁월한 Gemini를 사용합니다.
|
||||
- **document-writer** (`google/gemini-3-pro-preview`): 기술 문서 전문가라는 설정을 갖고 있습니다. Gemini 는 문학가입니다. 글을 기가막히게 씁니다.
|
||||
- **multimodal-looker** (`google/gemini-2.5-flash`): 시각적 콘텐츠 해석을 위한 전문 에이전트. PDF, 이미지, 다이어그램을 분석하여 정보를 추출합니다.
|
||||
|
||||
각 에이전트는 메인 에이전트가 알아서 호출하지만, 명시적으로 요청할 수도 있습니다:
|
||||
|
||||
@@ -217,19 +249,33 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
||||
|
||||
에이전트의 모델, 프롬프트, 권한은 `oh-my-opencode.json`에서 커스텀할 수 있습니다. 자세한 내용은 [설정](#설정)을 참고하세요.
|
||||
|
||||
#### 서브 에이전트 오케스트레이션 (omo_task)
|
||||
### 백그라운드 에이전트: 진짜 팀 처럼 일 하도록
|
||||
|
||||
`omo_task` 도구를 사용하면 에이전트(`oracle`, `frontend-ui-ux-engineer` 등)가 `explore`나 `librarian`을 서브 에이전트로 호출하여 특정 작업을 위임할 수 있습니다. 이를 통해 에이전트가 작업을 진행하기 전에 전문화된 다른 에이전트에게 정보를 요청하는 강력한 워크플로우가 가능합니다.
|
||||
위의 에이전트들을 미친듯이 한순간도 놀리지 않고 굴릴 수 있다면 어떨까요?
|
||||
|
||||
> **참고**: 무한 재귀를 방지하기 위해 `explore`와 `librarian` 에이전트는 `omo_task` 도구를 직접 사용할 수 없습니다.
|
||||
- GPT 에게 디버깅을 시켜놓고, Claude 가 다양한 시도를 해보며 직접 문제를 찾아보는 워크플로우
|
||||
- Gemini 가 프론트엔드를 작성하는 동안, Claude 가 백엔드를 작성하는 워크플로우
|
||||
- 다량의 병렬 탐색을 진행시켜놓고, 일단 해당 부분은 제외하고 먼저 구현을 진행하다, 탐색 내용을 바탕으로 구현을 마무리하는 워크플로우
|
||||
|
||||
### Tools
|
||||
이 워크플로우가 OhMyOpenCode 에서는 가능합니다.
|
||||
|
||||
#### 내장 LSP Tools
|
||||
서브 에이전트를 백그라운드에서 실행 할 수 있습니다. 이러면 메인 에이전트는 작업이 완료되면 알게 됩니다. 필요하다면 결과를 기다릴 수 있습니다.
|
||||
|
||||
당신이 에디터에서 사용하는 그 기능을 다른 에이전트들은 사용하지 못합니다. Oh My OpenCode 는 당신만의 그 도구를 LLM Agent 에게 쥐어줍니다. 리팩토링하고, 탐색하고, 분석하는 모든 작업을 OpenCode 의 설정값을 그대로 사용하여 지원합니다.
|
||||
**에이전트가 당신의 팀이 일 하듯 일하게하세요**
|
||||
|
||||
[OpenCode 는 LSP 를 제공하지만](https://opencode.ai/docs/lsp/), 오로지 분석용으로만 제공합니다. 탐색과 리팩토링을 위한 도구는 OpenCode 와 동일한 스펙과 설정으로 Oh My OpenCode 가 제공합니다.
|
||||
### 도구: 당신의 동료가 더 좋은 도구를 갖고 일하도록
|
||||
|
||||
#### 왜 당신만 IDE 를 쓰나요?
|
||||
|
||||
Syntax Highlighting, Autocomplete, Refactoring, Navigation, Analysis, 그리고 이젠 에이전트가 코드를 짜게 하기까지..
|
||||
|
||||
**왜 당신만 사용하나요?**
|
||||
**에이전트가 그 도구를 사용한다면 더 코드를 잘 작성할텐데요.**
|
||||
|
||||
[OpenCode 는 LSP 를 제공하지만](https://opencode.ai/docs/lsp/), 오로지 분석용으로만 제공합니다.
|
||||
|
||||
당신이 에디터에서 사용하는 그 기능을 다른 에이전트들은 사용하지 못합니다.
|
||||
뛰어난 동료에게 좋은 도구를 쥐어주세요. 이제 리팩토링도, 탐색도, 분석도 에이전트가 제대로 할 수 있습니다.
|
||||
|
||||
- **lsp_hover**: 위치의 타입 정보, 문서, 시그니처 가져오기
|
||||
- **lsp_goto_definition**: 심볼 정의로 이동
|
||||
@@ -242,70 +288,11 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
||||
- **lsp_rename**: 워크스페이스 전체에서 심볼 이름 변경
|
||||
- **lsp_code_actions**: 사용 가능한 빠른 수정/리팩토링 가져오기
|
||||
- **lsp_code_action_resolve**: 코드 액션 적용
|
||||
|
||||
#### 내장 AST-Grep Tools
|
||||
- **ast_grep_search**: AST 인식 코드 패턴 검색 (25개 언어)
|
||||
- **ast_grep_replace**: AST 인식 코드 교체
|
||||
|
||||
#### Grep
|
||||
- **grep**: 안전 제한이 있는 콘텐츠 검색 (5분 타임아웃, 10MB 출력 제한). OpenCode의 내장 `grep` 도구를 대체합니다.
|
||||
- 기본 grep 도구는 시간제한이 걸려있지 않습니다. 대형 코드베이스에서 광범위한 패턴을 검색하면 CPU가 폭발하고 무한히 멈출 수 있습니다.
|
||||
- 이 도구는 엄격한 제한을 적용하며, 내장 `grep`을 완전히 대체합니다.
|
||||
|
||||
#### Glob
|
||||
|
||||
- **glob**: 타임아웃 보호가 있는 파일 패턴 매칭 (60초). OpenCode 내장 `glob` 도구를 대체합니다.
|
||||
- 기본 `glob`은 타임아웃이 없습니다. ripgrep이 멈추면 무한정 대기합니다.
|
||||
- 이 도구는 타임아웃을 강제하고 만료 시 프로세스를 종료합니다.
|
||||
|
||||
#### 내장 MCPs
|
||||
|
||||
- **websearch_exa**: Exa AI 웹 검색. 실시간 웹 검색과 콘텐츠 스크래핑을 수행합니다. 관련 웹사이트에서 LLM에 최적화된 컨텍스트를 반환합니다.
|
||||
- **context7**: 라이브러리 문서 조회. 정확한 코딩을 위해 최신 라이브러리 문서를 가져옵니다.
|
||||
|
||||
필요 없다면 `oh-my-opencode.json`에서 비활성화할 수 있습니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"disabled_mcps": ["websearch_exa"]
|
||||
}
|
||||
```
|
||||
|
||||
#### Background Task
|
||||
|
||||
장시간 실행되는 작업이나 복잡한 분석을 메인 세션을 차단하지 않고 백그라운드에서 실행합니다. 작업이 완료되면 시스템이 자동으로 알림을 보냅니다.
|
||||
|
||||
- **background_task**: 백그라운드 에이전트 작업을 시작합니다. 설명, 프롬프트, 에이전트 타입을 지정하면 즉시 task ID를 반환합니다.
|
||||
- **background_output**: 작업 진행 상황 확인(`block=false`) 또는 결과 대기(`block=true`). 최대 10분까지 커스텀 타임아웃을 지원합니다.
|
||||
- **background_cancel**: task ID로 실행 중인 백그라운드 작업을 취소합니다.
|
||||
|
||||
주요 기능:
|
||||
- **비동기 실행**: 복잡한 분석이나 연구 작업을 백그라운드에서 처리하면서 다른 작업 계속 가능
|
||||
- **자동 알림**: 백그라운드 작업 완료 시 메인 세션에 자동 알림
|
||||
- **상태 추적**: 도구 호출 횟수, 마지막 사용 도구 등 실시간 진행 상황 모니터링
|
||||
- **세션 격리**: 각 작업은 독립된 세션에서 실행
|
||||
|
||||
사용 예시:
|
||||
```
|
||||
1. 시작: background_task → task_id="bg_abc123" 반환
|
||||
2. 다른 작업 계속 진행
|
||||
3. 시스템 알림: "Task bg_abc123 completed"
|
||||
4. 결과 조회: background_output(task_id="bg_abc123") → 전체 결과 획득
|
||||
```
|
||||
|
||||
### Hooks
|
||||
|
||||
- **Todo Continuation Enforcer**: 에이전트가 멈추기 전 모든 TODO 항목을 완료하도록 강제합니다. LLM의 고질적인 "중도 포기" 문제를 방지합니다.
|
||||
- **Context Window Monitor**: [컨텍스트 윈도우 불안 관리](https://agentic-patterns.com/patterns/context-window-anxiety-management/) 패턴을 구현합니다.
|
||||
- 사용량이 70%를 넘으면 에이전트에게 아직 토큰이 충분하다고 상기시켜, 급하게 불완전한 작업을 하는 것을 완화합니다.
|
||||
- **Session Notification**: 에이전트가 작업을 마치면 OS 네이티브 알림을 보냅니다 (macOS, Linux, Windows).
|
||||
- **Session Recovery**: API 에러로부터 자동으로 복구하여 세션 안정성을 보장합니다. 네 가지 시나리오를 처리합니다:
|
||||
- **Tool Result Missing**: `tool_use` 블록이 있지만 `tool_result`가 없을 때 (ESC 인터럽트) → "cancelled" tool result 주입
|
||||
- **Thinking Block Order**: thinking 블록이 첫 번째여야 하는데 아닐 때 → 빈 thinking 블록 추가
|
||||
- **Thinking Disabled Violation**: thinking 이 비활성화인데 thinking 블록이 있을 때 → thinking 블록 제거
|
||||
- **Empty Content Message**: 메시지가 thinking/meta 블록만 있고 실제 내용이 없을 때 → 파일시스템을 통해 "(interrupted)" 텍스트 주입
|
||||
- **Comment Checker**: 코드 수정 후 불필요한 주석을 감지하여 보고합니다. BDD 패턴, 지시어, 독스트링 등 유효한 주석은 똑똑하게 제외하고, AI가 남긴 흔적을 제거하여 코드를 깨끗하게 유지합니다.
|
||||
- **Directory AGENTS.md Injector**: 파일을 읽을 때 `AGENTS.md` 내용을 자동으로 주입합니다. 파일 디렉토리부터 프로젝트 루트까지 탐색하며, 경로 상의 **모든** `AGENTS.md` 파일을 수집합니다. 중첩된 디렉토리별 지침을 지원합니다:
|
||||
#### Context is all you need.
|
||||
- **Directory AGENTS.md / README.md Injector**: 파일을 읽을 때 `AGENTS.md`, `README.md` 내용을 자동으로 주입합니다. 파일 디렉토리부터 프로젝트 루트까지 탐색하며, 경로 상의 **모든** `AGENTS.md` 파일을 수집합니다. 중첩된 디렉토리별 지침을 지원합니다:
|
||||
```
|
||||
project/
|
||||
├── AGENTS.md # 프로젝트 전체 컨텍스트
|
||||
@@ -315,9 +302,8 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
||||
│ ├── AGENTS.md # 컴포넌트 전용 컨텍스트
|
||||
│ └── Button.tsx # 이 파일을 읽으면 위 3개 AGENTS.md 모두 주입
|
||||
```
|
||||
`Button.tsx`를 읽으면 순서대로 주입됩니다: `project/AGENTS.md` → `src/AGENTS.md` → `components/AGENTS.md`. 각 디렉토리의 컨텍스트는 세션당 한 번만 주입됩니다. Claude Code의 CLAUDE.md 기능에서 영감을 받았습니다.
|
||||
- **Directory README.md Injector**: 파일을 읽을 때 `README.md` 내용을 자동으로 주입합니다. AGENTS.md Injector와 동일하게 동작하며, 파일 디렉토리부터 프로젝트 루트까지 탐색합니다. LLM 에이전트에게 프로젝트 문서 컨텍스트를 제공합니다. 각 디렉토리의 README는 세션당 한 번만 주입됩니다.
|
||||
- **Rules Injector**: 파일을 읽을 때 `.claude/rules/` 디렉토리의 규칙을 자동으로 주입합니다.
|
||||
`Button.tsx`를 읽으면 순서대로 주입됩니다: `project/AGENTS.md` → `src/AGENTS.md` → `components/AGENTS.md`. 각 디렉토리의 컨텍스트는 세션당 한 번만 주입됩니다.
|
||||
- **Conditional Rules Injector**: 모든 규칙이 항상 필요하진 않습니다. 특정 규칙을 만족한다면, 파일을 읽을 때 `.claude/rules/` 디렉토리의 규칙을 자동으로 주입합니다.
|
||||
- 파일 디렉토리부터 프로젝트 루트까지 상향 탐색하며, `~/.claude/rules/` (사용자) 경로도 포함합니다.
|
||||
- `.md` 및 `.mdc` 파일을 지원합니다.
|
||||
- Frontmatter의 `globs` 필드(glob 패턴)를 기반으로 매칭합니다.
|
||||
@@ -331,57 +317,29 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
||||
- Use PascalCase for interface names
|
||||
- Use camelCase for function names
|
||||
```
|
||||
- **Think Mode**: 확장된 사고(Extended Thinking)가 필요한 상황을 자동으로 감지하고 모드를 전환합니다. 사용자가 깊은 사고를 요청하는 표현(예: "think deeply", "ultrathink")을 감지하면, 추론 능력을 극대화하도록 모델 설정을 동적으로 조정합니다.
|
||||
- **Ultrawork Mode**: 사용자가 "ultrawork" 또는 "ulw" 키워드를 입력하면 자동으로 에이전트 오케스트레이션 가이드를 주입합니다. 메인 에이전트가 모든 가용한 전문 에이전트(탐색, 사서, 계획, UI)를 백그라운드 작업을 통해 병렬로 최대한 활용하도록 강제하며, 엄격한 TODO 추적 및 검증 프로토콜을 따르게 합니다.
|
||||
- **Anthropic Auto Compact**: Anthropic 모델 사용 시 컨텍스트 한계에 도달하면 대화 기록을 자동으로 압축하여 효율적으로 관리합니다.
|
||||
- **Empty Task Response Detector**: 서브 에이전트가 수행한 작업이 비어있거나 무의미한 응답을 반환하는 경우를 감지하여, 오류 없이 우아하게 처리합니다.
|
||||
- **Grep Output Truncator**: Grep 검색 결과가 너무 길어 컨텍스트를 장악해버리는 것을 방지하기 위해, 과도한 출력을 자동으로 자릅니다.
|
||||
- **Online**: 프로젝트 규칙이 전부는 아니겠죠. 확장 기능을 위한 내장 MCP를 제공합니다:
|
||||
- **context7**: 공식 문서 조회
|
||||
- **websearch_exa**: 실시간 웹 검색
|
||||
- **grep_app**: 공개 GitHub 저장소에서 초고속 코드 검색 (구현 예제 찾기에 최적)
|
||||
|
||||
필요 없는 훅이 있다면, `~/.config/opencode/oh-my-opencode.json` 혹은 `.opencode/oh-my-opencode.json`의 `disabled_hooks`를 사용하여 비활성화할 수 있습니다:
|
||||
#### 멀티모달을 다 활용하면서, 토큰은 덜 쓰도록.
|
||||
|
||||
```json
|
||||
{
|
||||
"disabled_hooks": ["session-notification", "comment-checker"]
|
||||
}
|
||||
```
|
||||
AmpCode 에서 영감을 받은 look_at 도구를, OhMyOpenCode 에서도 제공합니다.
|
||||
에이전트는 직접 파일을 읽어 큰 컨텍스트를 점유당하는 대신, 다른 에이전트를 내부적으로 활용하여 파일의 내용만 명확히 이해 할 수 있습니다.
|
||||
|
||||
사용 가능한 훅: `todo-continuation-enforcer`, `context-window-monitor`, `session-recovery`, `session-notification`, `comment-checker`, `grep-output-truncator`, `directory-agents-injector`, `directory-readme-injector`, `empty-task-response-detector`, `think-mode`, `ultrawork-mode`, `anthropic-auto-compact`, `rules-injector`, `background-notification`, `auto-update-checker`
|
||||
#### 멈출 수 없는 에이전트 루프
|
||||
- 내장 grep, glob 도구를 대체합니다. 기본 구현에서는 타임아웃이 없어 무한정 대기할 수 있습니다.
|
||||
|
||||
> **참고**: `disabled_hooks`는 Oh My OpenCode의 내장 훅을 제어합니다. Claude Code의 `settings.json` 훅을 비활성화하려면 `claude_code.hooks: false`를 대신 사용하세요 ([호환성 토글](#호환성-토글) 참고).
|
||||
|
||||
### Claude Code 호환성
|
||||
### Claude Code 호환성: 그냥 바로 OpenCode 로 오세요.
|
||||
|
||||
Oh My OpenCode는 Claude Code 설정과 완벽하게 호환됩니다. Claude Code를 사용하셨다면, 기존 설정을 그대로 사용할 수 있습니다.
|
||||
|
||||
#### 호환성 토글
|
||||
|
||||
특정 Claude Code 호환 기능을 비활성화하려면 `claude_code` 설정 객체를 사용하세요:
|
||||
|
||||
```json
|
||||
{
|
||||
"claude_code": {
|
||||
"mcp": false,
|
||||
"commands": false,
|
||||
"skills": false,
|
||||
"agents": false,
|
||||
"hooks": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 토글 | `false`일 때 로딩 비활성화 경로 | 영향 받지 않음 |
|
||||
|------|-------------------------------|---------------|
|
||||
| `mcp` | `~/.claude/.mcp.json`, `./.mcp.json`, `./.claude/.mcp.json` | 내장 MCP (context7, websearch_exa) |
|
||||
| `commands` | `~/.claude/commands/*.md`, `./.claude/commands/*.md` | `~/.config/opencode/command/`, `./.opencode/command/` |
|
||||
| `skills` | `~/.claude/skills/*/SKILL.md`, `./.claude/skills/*/SKILL.md` | - |
|
||||
| `agents` | `~/.claude/agents/*.md`, `./.claude/agents/*.md` | 내장 에이전트 (oracle, librarian 등) |
|
||||
| `hooks` | `~/.claude/settings.json`, `./.claude/settings.json`, `./.claude/settings.local.json` | - |
|
||||
|
||||
모든 토글은 기본값이 `true` (활성화)입니다. 완전한 Claude Code 호환성을 원하면 `claude_code` 객체를 생략하세요.
|
||||
Oh My OpenCode 에는 Claude Code 호환성 레이어가 존재합니다.
|
||||
Claude Code를 사용하셨다면, 기존 설정을 그대로 사용할 수 있습니다.
|
||||
|
||||
#### Hooks 통합
|
||||
|
||||
Claude Code의 `settings.json` 훅 시스템을 통해 커스텀 스크립트를 실행합니다. Oh My OpenCode는 다음 위치의 훅을 읽고 실행합니다:
|
||||
Claude Code의 `settings.json` 훅 시스템을 통해 커스텀 스크립트를 실행합니다.
|
||||
Oh My OpenCode는 다음 위치의 훅을 읽고 실행합니다:
|
||||
|
||||
- `~/.claude/settings.json` (사용자)
|
||||
- `./.claude/settings.json` (프로젝트)
|
||||
@@ -435,15 +393,48 @@ Claude Code의 `settings.json` 훅 시스템을 통해 커스텀 스크립트를
|
||||
|
||||
**Transcript**: 세션 활동이 `~/.claude/transcripts/`에 JSONL 형식으로 기록되어 재생 및 분석이 가능합니다.
|
||||
|
||||
> **`claude-code-*` 네이밍에 대해**: `src/features/claude-code-*/` 아래의 기능들은 Claude Code의 설정 시스템에서 마이그레이션되었습니다. 이 네이밍 규칙은 어떤 기능이 Claude Code에서 유래했는지 명확히 식별합니다.
|
||||
#### 호환성 토글
|
||||
|
||||
### 기타 편의 기능
|
||||
특정 Claude Code 호환 기능을 비활성화하려면 `claude_code` 설정 객체를 사용 할 수 도 있습니다:
|
||||
|
||||
- **Terminal Title**: 세션 상태에 따라 터미널 타이틀을 자동 업데이트합니다 (유휴 ○, 처리중 ◐, 도구 ⚡, 에러 ✖). tmux를 지원합니다.
|
||||
- **Session State**: 이벤트 훅과 터미널 타이틀 업데이트에 사용되는 중앙집중식 세션 추적 모듈입니다.
|
||||
```json
|
||||
{
|
||||
"claude_code": {
|
||||
"mcp": false,
|
||||
"commands": false,
|
||||
"skills": false,
|
||||
"agents": false,
|
||||
"hooks": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 토글 | `false`일 때 로딩 비활성화 경로 | 영향 받지 않음 |
|
||||
| ---------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------- |
|
||||
| `mcp` | `~/.claude/.mcp.json`, `./.mcp.json`, `./.claude/.mcp.json` | 내장 MCP (context7, websearch_exa) |
|
||||
| `commands` | `~/.claude/commands/*.md`, `./.claude/commands/*.md` | `~/.config/opencode/command/`, `./.opencode/command/` |
|
||||
| `skills` | `~/.claude/skills/*/SKILL.md`, `./.claude/skills/*/SKILL.md` | - |
|
||||
| `agents` | `~/.claude/agents/*.md`, `./.claude/agents/*.md` | 내장 에이전트 (oracle, librarian 등) |
|
||||
| `hooks` | `~/.claude/settings.json`, `./.claude/settings.json`, `./.claude/settings.local.json` | - |
|
||||
|
||||
모든 토글은 기본값이 `true` (활성화)입니다. 완전한 Claude Code 호환성을 원하면 `claude_code` 객체를 생략하세요.
|
||||
|
||||
### 에이전트들을 위한 것이 아니라, 당신을 위한 것
|
||||
|
||||
에이전트들이 행복해지면, 당신이 제일 행복해집니다, 그렇지만 저는 당신도 돕고싶습니다.
|
||||
|
||||
- **Ultrawork Mode**: 사용자가 "ultrawork" 또는 "ulw" 키워드를 입력하면 자동으로 에이전트 오케스트레이션 가이드를 주입합니다. 메인 에이전트가 모든 가용한 전문 에이전트(탐색, 사서, 계획, UI)를 백그라운드 작업을 통해 병렬로 최대한 활용하도록 강제하며, 엄격한 TODO 추적 및 검증 프로토콜을 따르게 합니다. 그냥 ultrawork 하세요. 말한 모든 기능이 최대로 활용되도록 에이전트가 최적화됩니다.
|
||||
- **Todo Continuation Enforcer**: 에이전트가 멈추기 전 모든 TODO 항목을 완료하도록 강제합니다. LLM의 고질적인 "중도 포기" 문제를 방지합니다.
|
||||
- **Comment Checker**: 학습 과정의 습관 때문일까요. LLM 들은 주석이 너무 많습니다. LLM 들이 쓸모없는 주석을 작성하지 않도록 상기시킵니다. BDD 패턴, 지시어, 독스트링 등 유효한 주석은 똑똑하게 제외하고, 그렇지 않는 주석들에 대해 해명을 요구하며 깔끔한 코드를 구성하게 합니다.
|
||||
- **Think Mode**: 확장된 사고(Extended Thinking)가 필요한 상황을 자동으로 감지하고 모드를 전환합니다. 사용자가 깊은 사고를 요청하는 표현(예: "think deeply", "ultrathink")을 감지하면, 추론 능력을 극대화하도록 모델 설정을 동적으로 조정합니다.
|
||||
- **Context Window Monitor**: [컨텍스트 윈도우 불안 관리](https://agentic-patterns.com/patterns/context-window-anxiety-management/) 패턴을 구현합니다.
|
||||
- 사용량이 70%를 넘으면 에이전트에게 아직 토큰이 충분하다고 상기시켜, 급하게 불완전한 작업을 하는 것을 완화합니다.
|
||||
- OpenCode 에서 누락되거나 부족하다고 느끼는 안정성 보강 기능들이 내장되어있습니다. 클로드 코드에서의 안정적인 경험을 그대로 가져갈 수 있습니다. 돌다가 세션이 망가지지 않습니다. 망가져도 복구됩니다.
|
||||
|
||||
## 설정
|
||||
|
||||
비록 Highly Opinionated 한 설정이지만, 여러분의 입맛대로 조정 할 수 있습니다.
|
||||
|
||||
설정 파일 위치 (우선순위 순):
|
||||
1. `.opencode/oh-my-opencode.json` (프로젝트)
|
||||
2. `~/.config/opencode/oh-my-opencode.json` (사용자)
|
||||
@@ -500,13 +491,17 @@ Google Gemini 모델을 위한 내장 Antigravity OAuth를 활성화합니다:
|
||||
|
||||
### MCPs
|
||||
|
||||
기본적으로 Context7, Exa MCP 를 지원합니다.
|
||||
기본적으로 Context7, Exa, grep.app MCP 를 지원합니다.
|
||||
|
||||
- **context7**: 라이브러리의 최신 공식 문서를 가져옵니다
|
||||
- **websearch_exa**: Exa AI 기반 실시간 웹 검색
|
||||
- **grep_app**: [grep.app](https://grep.app)을 통해 수백만 개의 공개 GitHub 저장소에서 초고속 코드 검색
|
||||
|
||||
이것이 마음에 들지 않는다면, ~/.config/opencode/oh-my-opencode.json 혹은 .opencode/oh-my-opencode.json 의 `disabled_mcps` 를 사용하여 비활성화할 수 있습니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"disabled_mcps": ["context7", "websearch_exa"]
|
||||
"disabled_mcps": ["context7", "websearch_exa", "grep_app"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -535,10 +530,20 @@ OpenCode 에서 지원하는 모든 LSP 구성 및 커스텀 설정 (opencode.js
|
||||
|
||||
각 서버는 다음을 지원합니다: `command`, `extensions`, `priority`, `env`, `initialization`, `disabled`.
|
||||
|
||||
|
||||
## 작성자의 노트
|
||||
|
||||
Oh My OpenCode 를 설치하세요. 복잡하게 OpenCode 구성을 만들지마세요.
|
||||
제가 밟아보고 경험한 문제들의 해답을 이 플러그인에 담았고, 그저 깔고 사용하면 됩니다. OpenCode 가 ArchLinux 라면, Oh My OpenCode 는 [Omarchy](https://omarchy.org/) 입니다.
|
||||
Oh My OpenCode 를 설치하세요.
|
||||
|
||||
저는 여태까지 $24,000 어치의 토큰을 오로지 개인 개발 목적으로 개인적으로 사용했습니다.
|
||||
다양한 도구를 시도해보고 끝까지 구성해보았습니다. 제 선택은 OpenCode 였습니다.
|
||||
|
||||
제가 밟아보고 경험한 문제들의 해답을 이 플러그인에 담았고, 그저 깔고 사용하면 됩니다.
|
||||
OpenCode 가 Debian / ArchLinux 라면, Oh My OpenCode 는 Ubuntu / [Omarchy](https://omarchy.org/) 입니다.
|
||||
|
||||
|
||||
[AmpCode](https://ampcode.com), [Claude Code](https://code.claude.com/docs/ko/overview) 에게 강한 영향과 영감을 받고, 그들의 기능을 그대로, 혹은 더 낫게 이 곳에 구현했습니다. 그리고 구현하고 있습니다.
|
||||
**Open**Code 이니까요.
|
||||
|
||||
다른 에이전트 하니스 제공자들이 이야기하는 다중 모델, 안정성, 풍부한 기능을 그저 OpenCode 에서 누리세요.
|
||||
제가 테스트하고, 이 곳에 업데이트 하겠습니다. 저는 이 프로젝트의 가장 열렬한 사용자이기도 하니까요.
|
||||
@@ -550,13 +555,21 @@ Oh My OpenCode 를 설치하세요. 복잡하게 OpenCode 구성을 만들지마
|
||||
- 주로 겪는 상황에 맞는 빠른 모델은 무엇인지
|
||||
- 다른 에이전트 하니스에 제공되는 새로운 기능은 무엇인지.
|
||||
|
||||
고민하지마세요. 제가 고민할거고, 다른 사람들의 경험을 차용해 올것이고, 그래서 이 곳에 업데이트 하겠습니다.
|
||||
이 플러그인은 그 경험들의 하이라이트입니다. 여러분은 그저 최고를 취하세요. 만약 더 나은 제안이 있다면 언제든 기여에 열려있습니다.
|
||||
|
||||
**Agent Harness 에 대해 고민하지마세요.**
|
||||
**제가 고민할거고, 다른 사람들의 경험을 차용해 올것이고, 그래서 이 곳에 업데이트 하겠습니다.**
|
||||
|
||||
이 글이 오만하다고 느껴지고, 더 나은 해답이 있다면, 편히 기여해주세요. 환영합니다.
|
||||
|
||||
지금 시점에 여기에 언급된 어떤 프로젝트와 모델하고도 관련이 있지 않습니다. 온전히 개인적인 실험과 선호를 바탕으로 이 플러그인을 만들었습니다.
|
||||
|
||||
OpenCode 를 사용하여 이 프로젝트의 99% 를 작성했습니다. 기능 위주로 테스트했고, 저는 TS 를 제대로 작성 할 줄 모릅니다. **그치만 이 문서는 제가 직접 검토하고 전반적으로 다시 작성했으니 안심하고 읽으셔도 됩니다.**
|
||||
|
||||
## 주의
|
||||
|
||||
- 생산성이 너무 올라 갈 수 있습니다. 옆자리 동료한테 들키지 않도록 조심하세요.
|
||||
- 그렇지만 제가 소문 내겠습니다. 누가 이기나 내기해봅시다.
|
||||
- [1.0.132](https://github.com/sst/opencode/releases/tag/v1.0.132) 혹은 이것보다 낮은 버전을 사용중이라면, OpenCode 의 버그로 인해 제대로 구성이 되지 않을 수 있습니다.
|
||||
- [이를 고치는 PR 이 1.0.132 배포 이후에 병합되었으므로](https://github.com/sst/opencode/pull/5040) 이 변경사항이 포함된 최신 버전을 사용해주세요.
|
||||
- TMI: PR 도 OhMyOpenCode 의 셋업의 Librarian, Explore, Oracle 을 활용하여 우연히 발견하고 해결되었습니다.
|
||||
|
||||
509
README.md
509
README.md
@@ -1,71 +1,103 @@
|
||||
English | [한국어](README.ko.md)
|
||||
[English](README.md) | [한국어](README.ko.md)
|
||||
|
||||
## Contents
|
||||
|
||||
- [Oh My OpenCode](#oh-my-opencode)
|
||||
- [TL;DR](#tldr)
|
||||
- [Skip Reading This](#skip-reading-this)
|
||||
- [It's the Age of Agents](#its-the-age-of-agents)
|
||||
- [10 Minutes to Unlock](#10-minutes-to-unlock)
|
||||
- [Installation](#installation)
|
||||
- [For LLM Agents](#for-llm-agents)
|
||||
- [Why OpenCode & Why Oh My OpenCode](#why-opencode--why-oh-my-opencode)
|
||||
- [For Humans](#for-humans)
|
||||
- [For LLM Agents](#for-llm-agents)
|
||||
- [Step 1: Verify OpenCode Installation](#step-1-verify-opencode-installation)
|
||||
- [Step 2: Configure oh-my-opencode Plugin](#step-2-configure-oh-my-opencode-plugin)
|
||||
- [Step 3: Verify Setup](#step-3-verify-setup)
|
||||
- [Step 4: Configure Authentication](#step-4-configure-authentication)
|
||||
- [4.1 Anthropic (Claude)](#41-anthropic-claude)
|
||||
- [4.2 Google Gemini (Antigravity OAuth)](#42-google-gemini-antigravity-oauth)
|
||||
- [4.3 OpenAI (ChatGPT Plus/Pro)](#43-openai-chatgpt-pluspro)
|
||||
- [4.3.1 Model Configuration](#431-model-configuration)
|
||||
- [⚠️ Warning](#️-warning)
|
||||
- [Features](#features)
|
||||
- [Agents](#agents)
|
||||
- [Tools](#tools)
|
||||
- [Built-in LSP Tools](#built-in-lsp-tools)
|
||||
- [Built-in AST-Grep Tools](#built-in-ast-grep-tools)
|
||||
- [Grep](#grep)
|
||||
- [Built-in MCPs](#built-in-mcps)
|
||||
- [Background Task](#background-task)
|
||||
- [Hooks](#hooks)
|
||||
- [Claude Code Compatibility](#claude-code-compatibility)
|
||||
- [Other Features](#other-features)
|
||||
- [Agents: Your Teammates](#agents-your-teammates)
|
||||
- [Background Agents: Work Like a Team](#background-agents-work-like-a-team)
|
||||
- [The Tools: Your Teammates Deserve Better](#the-tools-your-teammates-deserve-better)
|
||||
- [Why Are You the Only One Using an IDE?](#why-are-you-the-only-one-using-an-ide)
|
||||
- [Context Is All You Need](#context-is-all-you-need)
|
||||
- [Be Multimodal. Save Tokens.](#be-multimodal-save-tokens)
|
||||
- [I Removed Their Blockers](#i-removed-their-blockers)
|
||||
- [Goodbye Claude Code. Hello Oh My OpenCode.](#goodbye-claude-code-hello-oh-my-opencode)
|
||||
- [Hooks Integration](#hooks-integration)
|
||||
- [Config Loaders](#config-loaders)
|
||||
- [Data Storage](#data-storage)
|
||||
- [Compatibility Toggles](#compatibility-toggles)
|
||||
- [Not Just for the Agents](#not-just-for-the-agents)
|
||||
- [Configuration](#configuration)
|
||||
- [Google Auth](#google-auth)
|
||||
- [Agents](#agents)
|
||||
- [MCPs](#mcps)
|
||||
- [LSP](#lsp)
|
||||
- [Author's Note](#authors-note)
|
||||
- [Warnings](#warnings)
|
||||
|
||||
# Oh My OpenCode
|
||||
|
||||
Oh My OpenCode
|
||||
|
||||
oMoMoMoMoMo···
|
||||
|
||||
If you work in tech, you likely appreciated [Claude Code](https://www.claude.com/product/claude-code).
|
||||
If you are a hacker, you will fucking fall in love with [OpenCode](https://github.com/sst/opencode).
|
||||
|
||||
Do you remember the first time you switched from Windows to Linux? The excitement of configuring everything just the way you wanted?
|
||||
In an era where that romance feels lost, OpenCode will be that project for you.
|
||||
If you love coding and computers, OpenCode will feel exactly like that moment you first touched Linux after years of Windows.
|
||||
Even if you don't, invest a little time. Boost your skills and productivity by multipliers.
|
||||
[Claude Code](https://www.claude.com/product/claude-code) is great.
|
||||
But if you're a hacker, you'll fall head over heels for [OpenCode](https://github.com/sst/opencode).
|
||||
|
||||
## TL;DR
|
||||
- Endlessly extensible. Endlessly customizable.
|
||||
- Zero screen flicker.
|
||||
- [LSP](https://opencode.ai/docs/lsp/), [linters, formatters](https://opencode.ai/docs/formatters/) activate automatically per file—and you can tweak everything.
|
||||
- Mix and match models. **Orchestrate them by purpose.**
|
||||
- Feature-rich. Beautiful. The terminal doesn't choke. High performance.
|
||||
|
||||
- **Model Setup Required**
|
||||
- This plugin utilizes models from [OpenCode Zen](https://opencode.ai/docs/zen/), Google, OpenAI, and Anthropic.
|
||||
- For Anthropic models, use [OpenCode's built-in Claude Code Max Plan login](https://opencode.ai/docs/providers/#anthropic).
|
||||
- For OpenAI models (ChatGPT Plus/Pro), install the [OpenCode-OpenAI-Codex-Auth plugin](https://github.com/numman-ali/opencode-openai-codex-auth).
|
||||
- For Google Gemini models, enable `google_auth: true` in `oh-my-opencode.json` for **built-in Antigravity OAuth**.
|
||||
- For other providers, [VibeProxy (macOS Only)](https://github.com/automazeio/vibeproxy) or [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) remain available as alternatives.
|
||||
- **Truth be told, the rest is optional. Just install it. Once set up, no further learning is required. It simply evolves.**
|
||||
- **Paste this document into Claude Code, OpenCode, or Cursor, instruct it to install, and ask for assistance if needed. It just installs.**
|
||||
- If you are an LLM Agent, install the required plugins and dependencies, then run `opencode auth login` via tmux to assist the user with configuration.
|
||||
Remember the first time you switched from Windows to Linux? That rush of configuring everything exactly how you wanted?
|
||||
In an age where that magic seems lost, OpenCode brings it back.
|
||||
If you love coding and computers, OpenCode is that Linux-after-Windows moment.
|
||||
|
||||
**But here's the catch: there's a lot to learn, it's complex, and your time is expensive.**
|
||||
|
||||
I've fixed that.
|
||||
Even if you're not a hacker, invest a few minutes. Multiply your skills and productivity.
|
||||
Hand this doc to an agent and let them set it up.
|
||||
|
||||
## Skip Reading This
|
||||
|
||||
### It's the Age of Agents
|
||||
- **Just paste this link into Claude Code / AmpCode / Factory Droid / Cursor and ask it to explain.**
|
||||
- Ask why it's good, why it's not just another boilerplate setup, what actually gets better.
|
||||
- If you're an LLM agent, explain to the user what becomes possible right now with this project.
|
||||
|
||||
### 10 Minutes to Unlock
|
||||
|
||||
1. **While Gemini 3 Pro writes the frontend as a background task, Claude Opus 4.5 handles the backend. Stuck debugging? Call GPT 5.2 for help. When the frontend reports done, verify and ship.**
|
||||
2. Need to look something up? It scours official docs, your entire codebase history, and public GitHub implementations—using not just grep but built-in LSP tools and AST-Grep.
|
||||
3. Stop worrying about context management when delegating to LLMs. I've got it covered.
|
||||
- OhMyOpenCode aggressively leverages multiple agents to lighten the context load.
|
||||
- **Your agent is now the dev team lead. You're the AI Manager.**
|
||||
4. It doesn't stop until the job is done.
|
||||
|
||||
|
||||
- Throw complex, massive tasks at it.
|
||||
- Drop the "ultrawork" keyword in your prompt and it handles everything automatically.
|
||||
|
||||
## Installation
|
||||
|
||||
Add to `~/.config/opencode/opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugin": [
|
||||
"oh-my-opencode"
|
||||
]
|
||||
}
|
||||
```
|
||||
### For Humans
|
||||
|
||||
## For LLM Agents
|
||||
Let the LLM Agent handle the grunt work. Paste this link and ask it to install and configure.
|
||||
|
||||
### For LLM Agents
|
||||
|
||||
<details>
|
||||
<summary>For LLM Agents (Click to expand)</summary>
|
||||
<summary>Instructions for LLM Agents (click to expand)</summary>
|
||||
|
||||
If you're an LLM Agent helping set up oh-my-opencode, follow these steps:
|
||||
|
||||
If you are an LLM Agent helping a user set up oh-my-opencode, follow these steps:
|
||||
|
||||
### Step 1: Verify OpenCode Installation
|
||||
|
||||
@@ -74,11 +106,11 @@ if command -v opencode &> /dev/null; then
|
||||
echo "OpenCode $(opencode --version) is installed"
|
||||
else
|
||||
echo "OpenCode is not installed. Please install it first."
|
||||
echo "See: https://opencode.ai/docs"
|
||||
echo "Ref: https://opencode.ai/docs"
|
||||
fi
|
||||
```
|
||||
|
||||
If OpenCode is not installed, guide the user to [OpenCode Installation Guide](https://opencode.ai/docs).
|
||||
If OpenCode isn't installed, check the [OpenCode Installation Guide](https://opencode.ai/docs). To save context, spawn a subagent to handle installation and report back.
|
||||
|
||||
### Step 2: Configure oh-my-opencode Plugin
|
||||
|
||||
@@ -92,10 +124,10 @@ if [ -f ~/.config/opencode/opencode.json ]; then
|
||||
~/.config/opencode/opencode.json > /tmp/oc.json && \
|
||||
mv /tmp/oc.json ~/.config/opencode/opencode.json
|
||||
else
|
||||
echo "Add \"oh-my-opencode\" to plugin array in ~/.config/opencode/opencode.json"
|
||||
echo "Please add \"oh-my-opencode\" to the plugin array in ~/.config/opencode/opencode.json"
|
||||
fi
|
||||
else
|
||||
# New config
|
||||
# Create new config
|
||||
echo '{"plugin":["oh-my-opencode"]}' > ~/.config/opencode/opencode.json
|
||||
fi
|
||||
```
|
||||
@@ -103,28 +135,28 @@ fi
|
||||
### Step 3: Verify Setup
|
||||
|
||||
```bash
|
||||
opencode --version # Should be >= 1.0.132
|
||||
opencode --version # Should be 1.0.132 or higher
|
||||
cat ~/.config/opencode/opencode.json # Should contain "oh-my-opencode" in plugin array
|
||||
```
|
||||
|
||||
### Step 4: Configure Authentication
|
||||
|
||||
Once `oh-my-opencode` is added to the plugin list, use `tmux` to assist the user with authentication setup. **It is RECOMMENDED to set up Google Auth (Step 4.2) by default.**
|
||||
Once oh-my-opencode is in the plugin list, use tmux to walk the user through authentication. Complete all steps below.
|
||||
|
||||
#### 4.1 Anthropic (Claude)
|
||||
|
||||
```bash
|
||||
opencode auth login
|
||||
# Select Provider: Anthropic
|
||||
# Select Login method: Claude Pro/Max
|
||||
# Complete OAuth flow in browser
|
||||
# Provider: Select Anthropic
|
||||
# Login method: Select Claude Pro/Max
|
||||
# Guide user through OAuth flow in browser
|
||||
# Wait for completion
|
||||
# Verify success and confirm with user
|
||||
```
|
||||
|
||||
#### 4.2 Google Gemini (Built-in Antigravity OAuth)
|
||||
#### 4.2 Google Gemini (Antigravity OAuth)
|
||||
|
||||
**This is the RECOMMENDED default configuration.**
|
||||
|
||||
First, proactively enable Google auth in `~/.config/opencode/oh-my-opencode.json` (or `.opencode/oh-my-opencode.json` for project-level):
|
||||
First, enable Google auth in `~/.config/opencode/oh-my-opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -136,9 +168,11 @@ Then authenticate:
|
||||
|
||||
```bash
|
||||
opencode auth login
|
||||
# Select Provider: Google
|
||||
# Select Login method: OAuth with Google (Antigravity)
|
||||
# Complete OAuth flow in browser
|
||||
# Provider: Select Google
|
||||
# Login method: Select OAuth with Google (Antigravity)
|
||||
# Guide user through OAuth flow in browser
|
||||
# Wait for completion
|
||||
# Verify success and confirm with user
|
||||
```
|
||||
|
||||
#### 4.3 OpenAI (ChatGPT Plus/Pro)
|
||||
@@ -154,156 +188,108 @@ First, add the opencode-openai-codex-auth plugin:
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: You must also configure the full model settings in your `opencode.json`. Copy the provider/models configuration from [`config/full-opencode.json`](https://github.com/numman-ali/opencode-openai-codex-auth/blob/main/config/full-opencode.json). See the [opencode-openai-codex-auth documentation](https://github.com/numman-ali/opencode-openai-codex-auth) for detailed setup instructions.
|
||||
**Important**: The official npm package currently has a bug causing 400 errors (`"No tool call found for function call output with call_id"`). **Use the hotfix branch** until fixed. Edit `~/.config/opencode/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"opencode-openai-codex-auth": "code-yeongyu/opencode-openai-codex-auth#fix/orphaned-function-call-output-with-tools"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run `cd ~/.config/opencode && bun i`. In `opencode.json`, use `"opencode-openai-codex-auth"` without the version suffix.
|
||||
|
||||
#### 4.3.1 Model Configuration
|
||||
You'll also need full model settings in `opencode.json`.
|
||||
Read the [opencode-openai-codex-auth documentation](https://github.com/numman-ali/opencode-openai-codex-auth), copy provider/models config from [`config/full-opencode.json`](https://github.com/numman-ali/opencode-openai-codex-auth/blob/main/config/full-opencode.json), and merge carefully to avoid breaking the user's existing setup.
|
||||
|
||||
Then authenticate:
|
||||
|
||||
```bash
|
||||
opencode auth login
|
||||
# Select Provider: OpenAI
|
||||
# Select Login method: ChatGPT Plus/Pro (Codex Subscription)
|
||||
# Complete OAuth flow in browser
|
||||
# Provider: Select OpenAI
|
||||
# Login method: Select ChatGPT Plus/Pro (Codex Subscription)
|
||||
# Guide user through OAuth flow in browser
|
||||
# Wait for completion
|
||||
# Verify success and confirm with user
|
||||
```
|
||||
|
||||
#### 4.4 Alternative: Proxy-based Authentication
|
||||
|
||||
For users who prefer proxy-based authentication, [VibeProxy](https://github.com/automazeio/vibeproxy) (macOS) or [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) remain available as alternatives.
|
||||
|
||||
### ⚠️ Warning
|
||||
|
||||
**Do NOT modify model settings or disable features (agents, hooks, MCPs) unless the user explicitly requests it.**
|
||||
**Don't change model settings or disable features (agents, hooks, MCPs) unless explicitly requested.**
|
||||
|
||||
The plugin works perfectly with default settings. While you SHOULD enable `google_auth: true` as recommended, do not alter other configurations or disable features without a specific request.
|
||||
The plugin works perfectly with defaults. Aside from the recommended `google_auth: true`, don't touch other settings without a specific ask.
|
||||
|
||||
</details>
|
||||
|
||||
## Why OpenCode & Why Oh My OpenCode
|
||||
|
||||
OpenCode is limitlessly extensible and customizable. Zero screen flicker.
|
||||
[LSP](https://opencode.ai/docs/lsp/), [linters, formatters](https://opencode.ai/docs/formatters/)? Automatic and fully configurable.
|
||||
You can mix and orchestrate models to your exact specifications.
|
||||
It is feature-rich. It is elegant. It handles the terminal without hesitation. It is high-performance.
|
||||
|
||||
But here is the catch: the learning curve is steep. There is a lot to master. And your time is expensive.
|
||||
|
||||
Inspired by [AmpCode](https://ampcode.com) and [Claude Code](https://code.claude.com/docs/en/overview), I have implemented their features here—often with superior execution.
|
||||
Because this is OpenCode.
|
||||
|
||||
Consider this a superior AmpCode, a superior Claude Code, or simply a specialized distribution.
|
||||
|
||||
I believe in the right tool for the job. For your wallet's sake, use CLIProxyAPI or VibeProxy. Employ the best LLMs from frontier labs. You are in command.
|
||||
|
||||
**Note**: This setup is highly opinionated. It represents the generic component of my personal configuration, so it evolves constantly. I have spent tokens worth $20,000 just for my personal programming usages, and this plugin represents the apex of that experience. You simply inherit the best. If you have superior ideas, PRs are welcome.
|
||||
|
||||
## Features
|
||||
|
||||
### Agents
|
||||
- **oracle** (`openai/gpt-5.2`): The architect. Expert in code reviews and strategy. Uses GPT-5.2 for its unmatched logic and reasoning capabilities. Inspired by AmpCode.
|
||||
- **librarian** (`anthropic/claude-haiku-4-5`): Multi-repo analysis, documentation lookup, and implementation examples. Haiku is chosen for its speed, competence, excellent tool usage, and cost-efficiency. Inspired by AmpCode.
|
||||
- **explore** (`opencode/grok-code`): Fast exploration and pattern matching. Claude Code uses Haiku; we use Grok. It is currently free, blazing fast, and intelligent enough for file traversal. Inspired by Claude Code.
|
||||
- **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): A designer turned developer. Creates stunning UIs. Uses Gemini because its creativity and UI code generation are superior.
|
||||
- **document-writer** (`google/gemini-3-pro-preview`): A technical writing expert. Gemini is a wordsmith; it writes prose that flows naturally.
|
||||
### Agents: Your Teammates
|
||||
|
||||
Each agent is automatically invoked by the main agent, but you can also explicitly request them:
|
||||
- **oracle** (`openai/gpt-5.2`): Architecture, code review, strategy. Uses GPT-5.2 for its stellar logical reasoning and deep analysis. Inspired by AmpCode.
|
||||
- **librarian** (`anthropic/claude-sonnet-4-5`): Multi-repo analysis, doc lookup, implementation examples. Claude Sonnet 4 is fast, smart, great at tool calls, and excellent for documentation research. Inspired by AmpCode.
|
||||
- **explore** (`opencode/grok-code`): Fast codebase exploration and pattern matching. Claude Code uses Haiku; we use Grok—it's free, blazing fast, and plenty smart for file traversal. Inspired by Claude Code.
|
||||
- **frontend-ui-ux-engineer** (`google/gemini-3-pro-preview`): A designer turned developer. Builds gorgeous UIs. Gemini excels at creative, beautiful UI code.
|
||||
- **document-writer** (`google/gemini-3-pro-preview`): Technical writing expert. Gemini is a wordsmith—writes prose that flows.
|
||||
- **multimodal-looker** (`google/gemini-2.5-flash`): Visual content specialist. Analyzes PDFs, images, diagrams to extract information.
|
||||
|
||||
The main agent invokes these automatically, but you can call them explicitly:
|
||||
|
||||
```
|
||||
@oracle Please think through the design of this part and suggest an architecture.
|
||||
@librarian Tell me how this is implemented — why does the behavior keep changing internally?
|
||||
@explore Tell me about the policy for this feature.
|
||||
Ask @oracle to review this design and propose an architecture
|
||||
Ask @librarian how this is implemented—why does the behavior keep changing?
|
||||
Ask @explore for the policy on this feature
|
||||
```
|
||||
|
||||
Agent models, prompts, and permissions can be customized via `oh-my-opencode.json`. See [Configuration](#configuration) for details.
|
||||
Customize agent models, prompts, and permissions in `oh-my-opencode.json`. See [Configuration](#configuration).
|
||||
|
||||
#### Subagent Orchestration (omo_task)
|
||||
### Background Agents: Work Like a Team
|
||||
|
||||
The `omo_task` tool allows agents (like `oracle`, `frontend-ui-ux-engineer`) to spawn `explore` or `librarian` as subagents to delegate specific tasks. This enables powerful workflows where an agent can "ask" another specialized agent to gather information before proceeding.
|
||||
What if you could run these agents relentlessly, never letting them idle?
|
||||
|
||||
> **Note**: To prevent infinite recursion, `explore` and `librarian` agents cannot use the `omo_task` tool themselves.
|
||||
- Have GPT debug while Claude tries different approaches to find the root cause
|
||||
- Gemini writes the frontend while Claude handles the backend
|
||||
- Kick off massive parallel searches, continue implementation on other parts, then finish using the search results
|
||||
|
||||
### Tools
|
||||
These workflows are possible with OhMyOpenCode.
|
||||
|
||||
#### Built-in LSP Tools
|
||||
Run subagents in the background. The main agent gets notified on completion. Wait for results if needed.
|
||||
|
||||
The features you use in your editor—other agents cannot access them. Oh My OpenCode hands those very tools to your LLM Agent. Refactoring, navigation, and analysis are all supported using the same OpenCode configuration.
|
||||
**Make your agents work like your team works.**
|
||||
|
||||
[OpenCode provides LSP](https://opencode.ai/docs/lsp/), but only for analysis. Oh My OpenCode equips you with navigation and refactoring tools matching the same specification.
|
||||
### The Tools: Your Teammates Deserve Better
|
||||
|
||||
- **lsp_hover**: Get type info, docs, signatures at position
|
||||
#### Why Are You the Only One Using an IDE?
|
||||
|
||||
Syntax highlighting, autocomplete, refactoring, navigation, analysis—and now agents writing code...
|
||||
|
||||
**Why are you the only one with these tools?**
|
||||
**Give them to your agents and watch them level up.**
|
||||
|
||||
[OpenCode provides LSP](https://opencode.ai/docs/lsp/), but only for analysis.
|
||||
|
||||
The features in your editor? Other agents can't touch them.
|
||||
Hand your best tools to your best colleagues. Now they can properly refactor, navigate, and analyze.
|
||||
|
||||
- **lsp_hover**: Type info, docs, signatures at position
|
||||
- **lsp_goto_definition**: Jump to symbol definition
|
||||
- **lsp_find_references**: Find all usages across workspace
|
||||
- **lsp_document_symbols**: Get file's symbol outline
|
||||
- **lsp_document_symbols**: Get file symbol outline
|
||||
- **lsp_workspace_symbols**: Search symbols by name across project
|
||||
- **lsp_diagnostics**: Get errors/warnings before build
|
||||
- **lsp_servers**: List available LSP servers
|
||||
- **lsp_prepare_rename**: Validate rename operation
|
||||
- **lsp_rename**: Rename symbol across workspace
|
||||
- **lsp_code_actions**: Get available quick fixes/refactorings
|
||||
- **lsp_code_action_resolve**: Apply a code action
|
||||
|
||||
#### Built-in AST-Grep Tools
|
||||
|
||||
- **lsp_code_action_resolve**: Apply code action
|
||||
- **ast_grep_search**: AST-aware code pattern search (25 languages)
|
||||
- **ast_grep_replace**: AST-aware code replacement
|
||||
|
||||
#### Grep
|
||||
|
||||
- **grep**: Content search with safety limits (5min timeout, 10MB output). Overrides OpenCode's built-in `grep` tool.
|
||||
- The default `grep` lacks safeguards. On a large codebase, a broad pattern can cause CPU overload and indefinite hanging.
|
||||
- This tool enforces strict limits and completely replaces the built-in `grep`.
|
||||
|
||||
#### Glob
|
||||
|
||||
- **glob**: File pattern matching with timeout protection (60s). Overrides OpenCode's built-in `glob` tool.
|
||||
- The default `glob` lacks timeout. If ripgrep hangs, it waits indefinitely.
|
||||
- This tool enforces timeouts and kills the process on expiration.
|
||||
|
||||
#### Built-in MCPs
|
||||
|
||||
- **websearch_exa**: Exa AI web search. Performs real-time web searches and can scrape content from specific URLs. Returns LLM-optimized context from relevant websites.
|
||||
- **context7**: Library documentation lookup. Fetches up-to-date documentation for any library to assist with accurate coding.
|
||||
|
||||
Don't need these? Disable them via `oh-my-opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"disabled_mcps": ["websearch_exa"]
|
||||
}
|
||||
```
|
||||
|
||||
#### Background Task
|
||||
|
||||
Run long-running or complex tasks in the background without blocking your main session. The system automatically notifies you when tasks complete.
|
||||
|
||||
- **background_task**: Launch a background agent task. Specify description, prompt, and agent type. Returns immediately with a task ID.
|
||||
- **background_output**: Check task progress (`block=false`) or wait for results (`block=true`). Supports custom timeout up to 10 minutes.
|
||||
- **background_cancel**: Cancel a running background task by task ID.
|
||||
|
||||
Key capabilities:
|
||||
- **Async Execution**: Offload complex analysis or research while you continue working
|
||||
- **Auto Notification**: System notifies the main session when background tasks complete
|
||||
- **Status Tracking**: Real-time progress with tool call counts and last tool used
|
||||
- **Session Isolation**: Each task runs in an independent session
|
||||
|
||||
Example workflow:
|
||||
```
|
||||
1. Launch: background_task → returns task_id="bg_abc123"
|
||||
2. Continue working on other tasks
|
||||
3. System notification: "Task bg_abc123 completed"
|
||||
4. Retrieve: background_output(task_id="bg_abc123") → get full results
|
||||
```
|
||||
|
||||
### Hooks
|
||||
|
||||
- **Todo Continuation Enforcer**: Forces the agent to complete all tasks before exiting. Eliminates the common LLM issue of "giving up halfway".
|
||||
- **Context Window Monitor**: Implements [Context Window Anxiety Management](https://agentic-patterns.com/patterns/context-window-anxiety-management/). When context usage exceeds 70%, it reminds the agent that resources are sufficient, preventing rushed or low-quality output.
|
||||
- **Session Notification**: Sends a native OS notification when the job is done (macOS, Linux, Windows).
|
||||
- **Session Recovery**: Automatically recovers from API errors, ensuring session stability. Handles four scenarios:
|
||||
- **Tool Result Missing**: When `tool_use` block exists without `tool_result` (ESC interrupt) → injects "cancelled" tool results
|
||||
- **Thinking Block Order**: When thinking block must be first but isn't → prepends empty thinking block
|
||||
- **Thinking Disabled Violation**: When thinking blocks exist but thinking is disabled → strips thinking blocks
|
||||
- **Empty Content Message**: When message has only thinking/meta blocks without actual content → injects "(interrupted)" text via filesystem
|
||||
- **Comment Checker**: Detects and reports unnecessary comments after code modifications. Smartly ignores valid patterns (BDD, directives, docstrings, shebangs) to keep the codebase clean from AI-generated artifacts.
|
||||
- **Directory AGENTS.md Injector**: Automatically injects `AGENTS.md` contents when reading files. Searches upward from the file's directory to project root, collecting **all** `AGENTS.md` files along the path hierarchy. This enables nested, directory-specific instructions:
|
||||
#### Context Is All You Need
|
||||
- **Directory AGENTS.md / README.md Injector**: Auto-injects `AGENTS.md` and `README.md` when reading files. Walks from file directory to project root, collecting **all** `AGENTS.md` files along the path. Supports nested directory-specific instructions:
|
||||
```
|
||||
project/
|
||||
├── AGENTS.md # Project-wide context
|
||||
@@ -311,16 +297,15 @@ Example workflow:
|
||||
│ ├── AGENTS.md # src-specific context
|
||||
│ └── components/
|
||||
│ ├── AGENTS.md # Component-specific context
|
||||
│ └── Button.tsx # Reading this injects ALL 3 AGENTS.md files
|
||||
│ └── Button.tsx # Reading this injects all 3 AGENTS.md files
|
||||
```
|
||||
When reading `Button.tsx`, the hook injects contexts in order: `project/AGENTS.md` → `src/AGENTS.md` → `components/AGENTS.md`. Each directory's context is injected only once per session. Inspired by Claude Code's CLAUDE.md feature.
|
||||
- **Directory README.md Injector**: Automatically injects `README.md` contents when reading files. Works identically to the AGENTS.md Injector, searching upward from the file's directory to project root. Provides project documentation context to the LLM agent. Each directory's README is injected only once per session.
|
||||
- **Rules Injector**: Automatically injects rules from `.claude/rules/` directory when reading files.
|
||||
- Searches upward from the file's directory to project root, plus `~/.claude/rules/` (user).
|
||||
Reading `Button.tsx` injects in order: `project/AGENTS.md` → `src/AGENTS.md` → `components/AGENTS.md`. Each directory's context is injected once per session.
|
||||
- **Conditional Rules Injector**: Not all rules apply all the time. Injects rules from `.claude/rules/` when conditions match.
|
||||
- Walks upward from file directory to project root, plus `~/.claude/rules/` (user).
|
||||
- Supports `.md` and `.mdc` files.
|
||||
- Frontmatter-based matching with `globs` field (glob patterns).
|
||||
- `alwaysApply: true` option for rules that should always apply.
|
||||
- Example rule file structure:
|
||||
- Matches via `globs` field in frontmatter.
|
||||
- `alwaysApply: true` for rules that should always fire.
|
||||
- Example rule file:
|
||||
```markdown
|
||||
---
|
||||
globs: ["*.ts", "src/**/*.js"]
|
||||
@@ -329,57 +314,29 @@ Example workflow:
|
||||
- Use PascalCase for interface names
|
||||
- Use camelCase for function names
|
||||
```
|
||||
- **Think Mode**: Automatic extended thinking detection and mode switching. Detects when user requests deep thinking (e.g., "think deeply", "ultrathink") and dynamically adjusts model settings for enhanced reasoning.
|
||||
- **Ultrawork Mode**: When user triggers "ultrawork" or "ulw" keywords, automatically injects agent orchestration guidance. Forces the main agent to leverage all available specialized agents (exploration, librarian, planning, UI) via background tasks in parallel, with strict TODO tracking and verification protocols.
|
||||
- **Anthropic Auto Compact**: Automatically compacts conversation history when approaching context limits for Anthropic models.
|
||||
- **Empty Task Response Detector**: Detects when subagent tasks return empty or meaningless responses and handles gracefully.
|
||||
- **Grep Output Truncator**: Prevents grep output from overwhelming the context by truncating excessively long results.
|
||||
- **Online**: Project rules aren't everything. Built-in MCPs for extended capabilities:
|
||||
- **context7**: Official documentation lookup
|
||||
- **websearch_exa**: Real-time web search
|
||||
- **grep_app**: Ultra-fast code search across public GitHub repos (great for finding implementation examples)
|
||||
|
||||
You can disable specific built-in hooks using `disabled_hooks` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
#### Be Multimodal. Save Tokens.
|
||||
|
||||
```json
|
||||
{
|
||||
"disabled_hooks": ["session-notification", "comment-checker"]
|
||||
}
|
||||
```
|
||||
The look_at tool from AmpCode, now in OhMyOpenCode.
|
||||
Instead of the agent reading massive files and bloating context, it internally leverages another agent to extract just what it needs.
|
||||
|
||||
Available hooks: `todo-continuation-enforcer`, `context-window-monitor`, `session-recovery`, `session-notification`, `comment-checker`, `grep-output-truncator`, `directory-agents-injector`, `directory-readme-injector`, `empty-task-response-detector`, `think-mode`, `ultrawork-mode`, `anthropic-auto-compact`, `rules-injector`, `background-notification`, `auto-update-checker`
|
||||
#### I Removed Their Blockers
|
||||
- Replaces built-in grep and glob tools. Default implementation has no timeout—can hang forever.
|
||||
|
||||
> **Note**: `disabled_hooks` controls Oh My OpenCode's built-in hooks. To disable Claude Code's `settings.json` hooks, use `claude_code.hooks: false` instead (see [Compatibility Toggles](#compatibility-toggles)).
|
||||
|
||||
### Claude Code Compatibility
|
||||
### Goodbye Claude Code. Hello Oh My OpenCode.
|
||||
|
||||
Oh My OpenCode provides seamless Claude Code configuration compatibility. If you've been using Claude Code, your existing setup works out of the box.
|
||||
|
||||
#### Compatibility Toggles
|
||||
|
||||
If you want to disable specific Claude Code compatibility features, use the `claude_code` configuration object:
|
||||
|
||||
```json
|
||||
{
|
||||
"claude_code": {
|
||||
"mcp": false,
|
||||
"commands": false,
|
||||
"skills": false,
|
||||
"agents": false,
|
||||
"hooks": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Toggle | When `false`, disables loading from... | NOT affected |
|
||||
|--------|----------------------------------------|--------------|
|
||||
| `mcp` | `~/.claude/.mcp.json`, `./.mcp.json`, `./.claude/.mcp.json` | Built-in MCPs (context7, websearch_exa) |
|
||||
| `commands` | `~/.claude/commands/*.md`, `./.claude/commands/*.md` | `~/.config/opencode/command/`, `./.opencode/command/` |
|
||||
| `skills` | `~/.claude/skills/*/SKILL.md`, `./.claude/skills/*/SKILL.md` | - |
|
||||
| `agents` | `~/.claude/agents/*.md`, `./.claude/agents/*.md` | Built-in agents (oracle, librarian, etc.) |
|
||||
| `hooks` | `~/.claude/settings.json`, `./.claude/settings.json`, `./.claude/settings.local.json` | - |
|
||||
|
||||
All toggles default to `true` (enabled). Omit the entire `claude_code` object for full Claude Code compatibility.
|
||||
Oh My OpenCode has a Claude Code compatibility layer.
|
||||
If you were using Claude Code, your existing config just works.
|
||||
|
||||
#### Hooks Integration
|
||||
|
||||
Execute custom scripts via Claude Code's `settings.json` hook system. Oh My OpenCode reads and executes hooks defined in:
|
||||
Run custom scripts via Claude Code's `settings.json` hook system.
|
||||
Oh My OpenCode reads and executes hooks from:
|
||||
|
||||
- `~/.claude/settings.json` (user)
|
||||
- `./.claude/settings.json` (project)
|
||||
@@ -388,7 +345,7 @@ Execute custom scripts via Claude Code's `settings.json` hook system. Oh My Open
|
||||
Supported hook events:
|
||||
- **PreToolUse**: Runs before tool execution. Can block or modify tool input.
|
||||
- **PostToolUse**: Runs after tool execution. Can add warnings or context.
|
||||
- **UserPromptSubmit**: Runs when user submits a prompt. Can block or inject messages.
|
||||
- **UserPromptSubmit**: Runs when user submits prompt. Can block or inject messages.
|
||||
- **Stop**: Runs when session goes idle. Can inject follow-up prompts.
|
||||
|
||||
Example `settings.json`:
|
||||
@@ -405,7 +362,7 @@ Example `settings.json`:
|
||||
}
|
||||
```
|
||||
|
||||
#### Configuration Loaders
|
||||
#### Config Loaders
|
||||
|
||||
**Command Loader**: Loads markdown-based slash commands from 4 directories:
|
||||
- `~/.claude/commands/` (user)
|
||||
@@ -421,7 +378,7 @@ Example `settings.json`:
|
||||
- `~/.claude/agents/*.md` (user)
|
||||
- `./.claude/agents/*.md` (project)
|
||||
|
||||
**MCP Loader**: Loads MCP server configurations from `.mcp.json` files:
|
||||
**MCP Loader**: Loads MCP server configs from `.mcp.json` files:
|
||||
- `~/.claude/.mcp.json` (user)
|
||||
- `./.mcp.json` (project)
|
||||
- `./.claude/.mcp.json` (local)
|
||||
@@ -429,24 +386,57 @@ Example `settings.json`:
|
||||
|
||||
#### Data Storage
|
||||
|
||||
**Todo Management**: Session todos are stored in Claude Code compatible format at `~/.claude/todos/`.
|
||||
**Todo Management**: Session todos stored in `~/.claude/todos/` in Claude Code compatible format.
|
||||
|
||||
**Transcript**: Session activity is logged to `~/.claude/transcripts/` in JSONL format, enabling replay and analysis.
|
||||
**Transcript**: Session activity logged to `~/.claude/transcripts/` in JSONL format for replay and analysis.
|
||||
|
||||
> **Note on `claude-code-*` naming**: Features under `src/features/claude-code-*/` are migrated from Claude Code's configuration system. This naming convention clearly identifies which features originated from Claude Code.
|
||||
#### Compatibility Toggles
|
||||
|
||||
### Other Features
|
||||
Disable specific Claude Code compatibility features with the `claude_code` config object:
|
||||
|
||||
- **Terminal Title**: Auto-updates terminal title with session status (idle ○, processing ◐, tool ⚡, error ✖). Supports tmux.
|
||||
- **Session State**: Centralized session tracking module used by event hooks and terminal title updates.
|
||||
```json
|
||||
{
|
||||
"claude_code": {
|
||||
"mcp": false,
|
||||
"commands": false,
|
||||
"skills": false,
|
||||
"agents": false,
|
||||
"hooks": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Toggle | When `false`, stops loading from... | Unaffected |
|
||||
| ---------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------- |
|
||||
| `mcp` | `~/.claude/.mcp.json`, `./.mcp.json`, `./.claude/.mcp.json` | Built-in MCP (context7, websearch_exa) |
|
||||
| `commands` | `~/.claude/commands/*.md`, `./.claude/commands/*.md` | `~/.config/opencode/command/`, `./.opencode/command/` |
|
||||
| `skills` | `~/.claude/skills/*/SKILL.md`, `./.claude/skills/*/SKILL.md` | - |
|
||||
| `agents` | `~/.claude/agents/*.md`, `./.claude/agents/*.md` | Built-in agents (oracle, librarian, etc.) |
|
||||
| `hooks` | `~/.claude/settings.json`, `./.claude/settings.json`, `./.claude/settings.local.json` | - |
|
||||
|
||||
All toggles default to `true` (enabled). Omit the `claude_code` object for full Claude Code compatibility.
|
||||
|
||||
### Not Just for the Agents
|
||||
|
||||
When agents thrive, you thrive. But I want to help you directly too.
|
||||
|
||||
- **Ultrawork Mode**: Type "ultrawork" or "ulw" and agent orchestration kicks in. Forces the main agent to max out all available specialists (explore, librarian, plan, UI) via background tasks in parallel, with strict TODO tracking and verification. Just ultrawork. Everything fires at full capacity.
|
||||
- **Todo Continuation Enforcer**: Makes agents finish all TODOs before stopping. Kills the chronic LLM habit of quitting halfway.
|
||||
- **Comment Checker**: LLMs love comments. Too many comments. This reminds them to cut the noise. Smartly ignores valid patterns (BDD, directives, docstrings) and demands justification for the rest. Clean code wins.
|
||||
- **Think Mode**: Auto-detects when extended thinking is needed and switches modes. Catches phrases like "think deeply" or "ultrathink" and dynamically adjusts model settings for maximum reasoning.
|
||||
- **Context Window Monitor**: Implements [Context Window Anxiety Management](https://agentic-patterns.com/patterns/context-window-anxiety-management/).
|
||||
- At 70%+ usage, reminds agents there's still headroom—prevents rushed, sloppy work.
|
||||
- Stability features that felt missing in OpenCode are built in. The Claude Code experience, transplanted. Sessions don't crash mid-run. Even if they do, they recover.
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration file locations (in priority order):
|
||||
Highly opinionated, but adjustable to taste.
|
||||
|
||||
Config file locations (priority order):
|
||||
1. `.opencode/oh-my-opencode.json` (project)
|
||||
2. `~/.config/opencode/oh-my-opencode.json` (user)
|
||||
|
||||
Schema autocomplete is supported:
|
||||
Schema autocomplete supported:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -464,7 +454,7 @@ Enable built-in Antigravity OAuth for Google Gemini models:
|
||||
}
|
||||
```
|
||||
|
||||
When enabled, `opencode auth login` will show "OAuth with Google (Antigravity)" as a login option for the Google provider.
|
||||
When enabled, `opencode auth login` shows "OAuth with Google (Antigravity)" for the Google provider.
|
||||
|
||||
### Agents
|
||||
|
||||
@@ -486,7 +476,7 @@ Override built-in agent settings:
|
||||
|
||||
Each agent supports: `model`, `temperature`, `top_p`, `prompt`, `tools`, `disable`, `description`, `mode`, `color`, `permission`.
|
||||
|
||||
Or you can disable them using `disabled_agents` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
Or disable via `disabled_agents` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -498,23 +488,27 @@ Available agents: `oracle`, `librarian`, `explore`, `frontend-ui-ux-engineer`, `
|
||||
|
||||
### MCPs
|
||||
|
||||
By default, Context7 and Exa MCP are supported.
|
||||
Context7, Exa, and grep.app MCP enabled by default.
|
||||
|
||||
If you don't want these, you can disable them using `disabled_mcps` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
- **context7**: Fetches up-to-date official documentation for libraries
|
||||
- **websearch_exa**: Real-time web search powered by Exa AI
|
||||
- **grep_app**: Ultra-fast code search across millions of public GitHub repositories via [grep.app](https://grep.app)
|
||||
|
||||
Don't want them? Disable via `disabled_mcps` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"disabled_mcps": ["context7", "websearch_exa"]
|
||||
"disabled_mcps": ["context7", "websearch_exa", "grep_app"]
|
||||
}
|
||||
```
|
||||
|
||||
### LSP
|
||||
|
||||
OpenCode provides LSP tools for analysis.
|
||||
Oh My OpenCode provides LSP tools for refactoring (rename, code actions).
|
||||
It supports all LSP configurations and custom settings supported by OpenCode (those configured in opencode.json), and you can also configure additional settings specifically for Oh My OpenCode as shown below.
|
||||
Oh My OpenCode adds refactoring tools (rename, code actions).
|
||||
All OpenCode LSP configs and custom settings (from opencode.json) are supported, plus additional Oh My OpenCode-specific settings.
|
||||
|
||||
You can configure additional LSP servers via the `lsp` option in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
Add LSP servers via the `lsp` option in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -533,29 +527,46 @@ You can configure additional LSP servers via the `lsp` option in `~/.config/open
|
||||
|
||||
Each server supports: `command`, `extensions`, `priority`, `env`, `initialization`, `disabled`.
|
||||
|
||||
|
||||
## Author's Note
|
||||
|
||||
Install Oh My OpenCode. Do not waste time configuring OpenCode from scratch.
|
||||
I have resolved the friction so you don't have to. The answers are in this plugin. If OpenCode is Arch Linux, Oh My OpenCode is [Omarchy](https://omarchy.org/).
|
||||
Install Oh My OpenCode.
|
||||
|
||||
Enjoy the multi-model stability and rich feature set that other harnesses promise but fail to deliver.
|
||||
I will continue testing and updating here. I am the primary user of this project.
|
||||
I've used LLMs worth $24,000 tokens purely for personal development.
|
||||
Tried every tool out there, configured them to death. OpenCode won.
|
||||
|
||||
- Who possesses the best raw logic?
|
||||
- Who is the debugging god?
|
||||
The answers to every problem I hit are baked into this plugin. Just install and go.
|
||||
If OpenCode is Debian/Arch, Oh My OpenCode is Ubuntu/[Omarchy](https://omarchy.org/).
|
||||
|
||||
|
||||
Heavily influenced by [AmpCode](https://ampcode.com) and [Claude Code](https://code.claude.com/docs/overview)—I've ported their features here, often improved. And I'm still building.
|
||||
It's **Open**Code, after all.
|
||||
|
||||
Enjoy multi-model orchestration, stability, and rich features that other harnesses promise but can't deliver.
|
||||
I'll keep testing and updating. I'm this project's most obsessive user.
|
||||
- Which model has the sharpest logic?
|
||||
- Who's the debugging god?
|
||||
- Who writes the best prose?
|
||||
- Who dominates frontend?
|
||||
- Who owns backend?
|
||||
- Which model is fastest for daily driving?
|
||||
- What new features are other harnesses shipping?
|
||||
|
||||
Do not overthink it. I have done the thinking. I will integrate the best practices. I will update this.
|
||||
If this sounds arrogant and you have a superior solution, send a PR. You are welcome.
|
||||
This plugin is the distillation of that experience. Just take the best. Got a better idea? PRs are welcome.
|
||||
|
||||
As of now, I have no affiliation with any of the projects or models mentioned here. This plugin is purely based on personal experimentation and preference.
|
||||
**Stop agonizing over agent harness choices.**
|
||||
**I'll do the research, borrow from the best, and ship updates here.**
|
||||
|
||||
If this sounds arrogant and you have a better answer, please contribute. You're welcome.
|
||||
|
||||
I have no affiliation with any project or model mentioned here. This is purely personal experimentation and preference.
|
||||
|
||||
99% of this project was built using OpenCode. I tested for functionality—I don't really know how to write proper TypeScript. **But I personally reviewed and largely rewrote this doc, so read with confidence.**
|
||||
|
||||
I constructed 99% of this project using OpenCode. I focused on functional verification, and honestly, I don't know how to write proper TypeScript. **But I personally reviewed and comprehensively rewritten this documentation, so you can rely on it with confidence.**
|
||||
## Warnings
|
||||
|
||||
- If you are on [1.0.132](https://github.com/sst/opencode/releases/tag/v1.0.132) or lower, OpenCode has a bug that might break config.
|
||||
- [The fix](https://github.com/sst/opencode/pull/5040) was merged after 1.0.132, so use a newer version.
|
||||
- Productivity might spike too hard. Don't let your coworker notice.
|
||||
- Actually, I'll spread the word. Let's see who wins.
|
||||
- If you're on [1.0.132](https://github.com/sst/opencode/releases/tag/v1.0.132) or older, an OpenCode bug may break config.
|
||||
- [The fix](https://github.com/sst/opencode/pull/5040) was merged after 1.0.132—use a newer version.
|
||||
- Fun fact: That PR was discovered and fixed thanks to OhMyOpenCode's Librarian, Explore, and Oracle setup.
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"librarian",
|
||||
"explore",
|
||||
"frontend-ui-ux-engineer",
|
||||
"document-writer"
|
||||
"document-writer",
|
||||
"multimodal-looker"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -63,7 +64,8 @@
|
||||
"librarian",
|
||||
"explore",
|
||||
"frontend-ui-ux-engineer",
|
||||
"document-writer"
|
||||
"document-writer",
|
||||
"multimodal-looker"
|
||||
]
|
||||
},
|
||||
"additionalProperties": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "oh-my-opencode",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.4",
|
||||
"description": "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -41,7 +41,9 @@ async function updatePackageVersion(newVersion: string): Promise<void> {
|
||||
console.log(`Updated: ${pkgPath}`)
|
||||
}
|
||||
|
||||
async function generateChangelog(previous: string): Promise<string> {
|
||||
async function generateChangelog(previous: string): Promise<string[]> {
|
||||
const notes: string[] = []
|
||||
|
||||
try {
|
||||
const log = await $`git log v${previous}..HEAD --oneline --format="%h %s"`.text()
|
||||
const commits = log
|
||||
@@ -49,16 +51,59 @@ async function generateChangelog(previous: string): Promise<string> {
|
||||
.filter((line) => line && !line.match(/^\w+ (ignore:|test:|chore:|ci:|release:)/i))
|
||||
|
||||
if (commits.length > 0) {
|
||||
const changelog = commits.map((c) => `- ${c}`).join("\n")
|
||||
for (const commit of commits) {
|
||||
notes.push(`- ${commit}`)
|
||||
}
|
||||
console.log("\n--- Changelog ---")
|
||||
console.log(changelog)
|
||||
console.log(notes.join("\n"))
|
||||
console.log("-----------------\n")
|
||||
return changelog
|
||||
}
|
||||
} catch {
|
||||
console.log("No previous tags found, skipping changelog generation")
|
||||
}
|
||||
return ""
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
async function getContributors(previous: string): Promise<string[]> {
|
||||
const notes: string[] = []
|
||||
|
||||
const team = ["actions-user", "github-actions[bot]", "code-yeongyu"]
|
||||
|
||||
try {
|
||||
const compare =
|
||||
await $`gh api "/repos/code-yeongyu/oh-my-opencode/compare/v${previous}...HEAD" --jq '.commits[] | {login: .author.login, message: .commit.message}'`.text()
|
||||
const contributors = new Map<string, string[]>()
|
||||
|
||||
for (const line of compare.split("\n").filter(Boolean)) {
|
||||
const { login, message } = JSON.parse(line) as { login: string | null; message: string }
|
||||
const title = message.split("\n")[0] ?? ""
|
||||
if (title.match(/^(ignore:|test:|chore:|ci:|release:)/i)) continue
|
||||
|
||||
if (login && !team.includes(login)) {
|
||||
if (!contributors.has(login)) contributors.set(login, [])
|
||||
contributors.get(login)?.push(title)
|
||||
}
|
||||
}
|
||||
|
||||
if (contributors.size > 0) {
|
||||
notes.push("")
|
||||
notes.push(`**Thank you to ${contributors.size} community contributor${contributors.size > 1 ? "s" : ""}:**`)
|
||||
for (const [username, userCommits] of contributors) {
|
||||
notes.push(`- @${username}:`)
|
||||
for (const commit of userCommits) {
|
||||
notes.push(` - ${commit}`)
|
||||
}
|
||||
}
|
||||
console.log("\n--- Contributors ---")
|
||||
console.log(notes.join("\n"))
|
||||
console.log("--------------------\n")
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Failed to fetch contributors:", error)
|
||||
}
|
||||
|
||||
return notes
|
||||
}
|
||||
|
||||
async function buildAndPublish(): Promise<void> {
|
||||
@@ -71,7 +116,7 @@ async function buildAndPublish(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function gitTagAndRelease(newVersion: string, changelog: string): Promise<void> {
|
||||
async function gitTagAndRelease(newVersion: string, notes: string[]): Promise<void> {
|
||||
if (!process.env.CI) return
|
||||
|
||||
console.log("\nCommitting and tagging...")
|
||||
@@ -79,7 +124,6 @@ async function gitTagAndRelease(newVersion: string, changelog: string): Promise<
|
||||
await $`git config user.name "github-actions[bot]"`
|
||||
await $`git add package.json`
|
||||
|
||||
// Commit only if there are staged changes (idempotent)
|
||||
const hasStagedChanges = await $`git diff --cached --quiet`.nothrow()
|
||||
if (hasStagedChanges.exitCode !== 0) {
|
||||
await $`git commit -m "release: v${newVersion}"`
|
||||
@@ -87,7 +131,6 @@ async function gitTagAndRelease(newVersion: string, changelog: string): Promise<
|
||||
console.log("No changes to commit (version already updated)")
|
||||
}
|
||||
|
||||
// Tag only if it doesn't exist (idempotent)
|
||||
const tagExists = await $`git rev-parse v${newVersion}`.nothrow()
|
||||
if (tagExists.exitCode !== 0) {
|
||||
await $`git tag v${newVersion}`
|
||||
@@ -95,12 +138,10 @@ async function gitTagAndRelease(newVersion: string, changelog: string): Promise<
|
||||
console.log(`Tag v${newVersion} already exists`)
|
||||
}
|
||||
|
||||
// Push (idempotent - git push is already idempotent)
|
||||
await $`git push origin HEAD --tags`
|
||||
|
||||
// Create release only if it doesn't exist (idempotent)
|
||||
console.log("\nCreating GitHub release...")
|
||||
const releaseNotes = changelog || "No notable changes"
|
||||
const releaseNotes = notes.length > 0 ? notes.join("\n") : "No notable changes"
|
||||
const releaseExists = await $`gh release view v${newVersion}`.nothrow()
|
||||
if (releaseExists.exitCode !== 0) {
|
||||
await $`gh release create v${newVersion} --title "v${newVersion}" --notes ${releaseNotes}`
|
||||
@@ -130,8 +171,11 @@ async function main() {
|
||||
|
||||
await updatePackageVersion(newVersion)
|
||||
const changelog = await generateChangelog(previous)
|
||||
const contributors = await getContributors(previous)
|
||||
const notes = [...changelog, ...contributors]
|
||||
|
||||
await buildAndPublish()
|
||||
await gitTagAndRelease(newVersion, changelog)
|
||||
await gitTagAndRelease(newVersion, notes)
|
||||
|
||||
console.log(`\n=== Successfully published ${PACKAGE_NAME}@${newVersion} ===`)
|
||||
}
|
||||
|
||||
77
src/agents/build.ts
Normal file
77
src/agents/build.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
export const BUILD_AGENT_PROMPT_EXTENSION = `
|
||||
# Agent Orchestration & Task Management
|
||||
|
||||
You are not just a coder - you are an **ORCHESTRATOR**. Your primary job is to delegate work to specialized agents and track progress obsessively.
|
||||
|
||||
## Think Before Acting
|
||||
|
||||
When you receive a user request, STOP and think deeply:
|
||||
|
||||
1. **What specialized agents can handle this better than me?**
|
||||
- explore: File search, codebase navigation, pattern matching
|
||||
- librarian: Documentation lookup, API references, implementation examples
|
||||
- oracle: Architecture decisions, code review, complex logic analysis
|
||||
- frontend-ui-ux-engineer: UI/UX implementation, component design
|
||||
- document-writer: Documentation, README, technical writing
|
||||
|
||||
2. **Can I parallelize this work?**
|
||||
- Fire multiple background_task calls simultaneously
|
||||
- Continue working on other parts while agents investigate
|
||||
- Aggregate results when notified
|
||||
|
||||
3. **Have I planned this in my TODO list?**
|
||||
- Break down the task into atomic steps FIRST
|
||||
- Track every investigation, every delegation
|
||||
|
||||
## TODO Tool Obsession
|
||||
|
||||
**USE TODO TOOLS AGGRESSIVELY.** This is non-negotiable.
|
||||
|
||||
### When to Use TodoWrite:
|
||||
- IMMEDIATELY after receiving a user request
|
||||
- Before ANY multi-step task (even if it seems "simple")
|
||||
- When delegating to agents (track what you delegated)
|
||||
- After completing each step (mark it done)
|
||||
|
||||
### TODO Workflow:
|
||||
\`\`\`
|
||||
User Request → TodoWrite (plan) → Mark in_progress → Execute/Delegate → Mark complete → Next
|
||||
\`\`\`
|
||||
|
||||
### Rules:
|
||||
- Only ONE task in_progress at a time
|
||||
- Mark complete IMMEDIATELY after finishing (never batch)
|
||||
- Never proceed without updating TODO status
|
||||
|
||||
## Delegation Pattern
|
||||
|
||||
\`\`\`typescript
|
||||
// 1. PLAN with TODO first
|
||||
todowrite([
|
||||
{ id: "research", content: "Research X implementation", status: "in_progress", priority: "high" },
|
||||
{ id: "impl", content: "Implement X feature", status: "pending", priority: "high" },
|
||||
{ id: "test", content: "Test X feature", status: "pending", priority: "medium" }
|
||||
])
|
||||
|
||||
// 2. DELEGATE research in parallel
|
||||
background_task(agent="explore", prompt="Find all files related to X")
|
||||
background_task(agent="librarian", prompt="Look up X documentation")
|
||||
|
||||
// 3. CONTINUE working on implementation skeleton while agents research
|
||||
// 4. When notified, INTEGRATE findings and mark TODO complete
|
||||
\`\`\`
|
||||
|
||||
## Anti-Patterns (AVOID):
|
||||
- Doing everything yourself when agents can help
|
||||
- Skipping TODO planning for "quick" tasks
|
||||
- Forgetting to mark tasks complete
|
||||
- Sequential execution when parallel is possible
|
||||
- Direct tool calls without considering delegation
|
||||
|
||||
## Remember:
|
||||
- You are the **team lead**, not the grunt worker
|
||||
- Your context window is precious - delegate to preserve it
|
||||
- Agents have specialized expertise - USE THEM
|
||||
- TODO tracking gives users visibility into your progress
|
||||
- Parallel execution = faster results
|
||||
`;
|
||||
@@ -6,7 +6,7 @@ export const exploreAgent: AgentConfig = {
|
||||
mode: "subagent",
|
||||
model: "opencode/grok-code",
|
||||
temperature: 0.1,
|
||||
tools: { write: false, edit: false },
|
||||
tools: { write: false, edit: false, bash: true, read: true },
|
||||
prompt: `You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
|
||||
|
||||
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
||||
@@ -73,6 +73,46 @@ Your response has FAILED if:
|
||||
- **Using Git CLI extensively for repository insights**
|
||||
- **Using LSP tools for semantic code analysis**
|
||||
- **Using AST-grep for structural code pattern matching**
|
||||
- **Using grep_app (grep.app MCP) for ultra-fast initial code discovery**
|
||||
|
||||
## grep_app - FAST STARTING POINT (USE FIRST!)
|
||||
|
||||
**grep_app is your fastest weapon for initial code discovery.** It searches millions of public GitHub repositories instantly.
|
||||
|
||||
### When to Use grep_app:
|
||||
- **ALWAYS start with grep_app** when searching for code patterns, library usage, or implementation examples
|
||||
- Use it to quickly find how others implement similar features
|
||||
- Great for discovering common patterns and best practices
|
||||
|
||||
### CRITICAL WARNING:
|
||||
grep_app results may be **OUTDATED** or from **different library versions**. You MUST:
|
||||
1. Use grep_app results as a **starting point only**
|
||||
2. **Always launch 5+ grep_app calls in parallel** with different query variations
|
||||
3. **Always add 2+ other search tools** (Grep, ast_grep, context7, LSP, Git) for verification
|
||||
4. Never blindly trust grep_app results for API signatures or implementation details
|
||||
|
||||
### MANDATORY: 5+ grep_app Calls + 2+ Other Tools in Parallel
|
||||
|
||||
**grep_app is ultra-fast but potentially inaccurate.** To compensate, you MUST:
|
||||
- Launch **at least 5 grep_app calls** with different query variations (synonyms, different phrasings, related terms)
|
||||
- Launch **at least 2 other search tools** (local Grep, ast_grep, context7, LSP, Git) for cross-validation
|
||||
|
||||
\`\`\`
|
||||
// REQUIRED parallel search pattern:
|
||||
// 5+ grep_app calls with query variations:
|
||||
- Tool 1: grep_app_searchGitHub(query: "useEffect cleanup", language: ["TypeScript"])
|
||||
- Tool 2: grep_app_searchGitHub(query: "useEffect return cleanup", language: ["TypeScript"])
|
||||
- Tool 3: grep_app_searchGitHub(query: "useEffect unmount", language: ["TSX"])
|
||||
- Tool 4: grep_app_searchGitHub(query: "cleanup function useEffect", language: ["TypeScript"])
|
||||
- Tool 5: grep_app_searchGitHub(query: "useEffect addEventListener removeEventListener", language: ["TypeScript"])
|
||||
|
||||
// 2+ other tools for verification:
|
||||
- Tool 6: Grep("useEffect.*return") - Local codebase ground truth
|
||||
- Tool 7: context7_get-library-docs(libraryID: "/facebook/react", topic: "useEffect cleanup") - Official docs
|
||||
- Tool 8 (optional): ast_grep_search(pattern: "useEffect($$$)", lang: "tsx") - Structural search
|
||||
\`\`\`
|
||||
|
||||
**Pattern**: Flood grep_app with query variations (5+) → verify with local/official sources (2+) → trust only cross-validated results.
|
||||
|
||||
## Git CLI - USE EXTENSIVELY
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { librarianAgent } from "./librarian"
|
||||
import { exploreAgent } from "./explore"
|
||||
import { frontendUiUxEngineerAgent } from "./frontend-ui-ux-engineer"
|
||||
import { documentWriterAgent } from "./document-writer"
|
||||
import { multimodalLookerAgent } from "./multimodal-looker"
|
||||
|
||||
export const builtinAgents: Record<string, AgentConfig> = {
|
||||
oracle: oracleAgent,
|
||||
@@ -11,7 +12,9 @@ export const builtinAgents: Record<string, AgentConfig> = {
|
||||
explore: exploreAgent,
|
||||
"frontend-ui-ux-engineer": frontendUiUxEngineerAgent,
|
||||
"document-writer": documentWriterAgent,
|
||||
"multimodal-looker": multimodalLookerAgent,
|
||||
}
|
||||
|
||||
export * from "./types"
|
||||
export { createBuiltinAgents } from "./utils"
|
||||
export { BUILD_AGENT_PROMPT_EXTENSION } from "./build"
|
||||
|
||||
@@ -4,9 +4,9 @@ export const librarianAgent: AgentConfig = {
|
||||
description:
|
||||
"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. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
|
||||
mode: "subagent",
|
||||
model: "anthropic/claude-haiku-4-5",
|
||||
model: "anthropic/claude-sonnet-4-5",
|
||||
temperature: 0.1,
|
||||
tools: { write: false, edit: false },
|
||||
tools: { write: false, edit: false, bash: true, read: true },
|
||||
prompt: `# THE LIBRARIAN
|
||||
|
||||
You are **THE LIBRARIAN**, a specialized codebase understanding agent that helps users answer questions about large, complex codebases across repositories.
|
||||
@@ -35,39 +35,45 @@ Your role is to provide thorough, comprehensive analysis and explanations of cod
|
||||
- Explain **WHY** it works that way by citing the actual implementation
|
||||
- Provide **permalinks** so users can verify your claims
|
||||
4. **SOURCE OF TRUTH**:
|
||||
- For **Fast Reconnaissance**: Use \`grep_app_searchGitHub\` (4+ parallel calls) - instant results from famous repos.
|
||||
- For **How-To**: Use \`context7\` (Official Docs) + verify with source code.
|
||||
- For **Real-World Usage**: Use \`gh search code\` (GitHub).
|
||||
- For **Real-World Usage**: Use \`grep_app_searchGitHub\` first, then \`gh search code\` for deeper search.
|
||||
- For **Internal Logic**: Clone repo to \`/tmp\` and read source directly.
|
||||
- For **Change History/Intent**: Use \`git log\` or \`git blame\` (Commit History).
|
||||
- For **Local Codebase Context**: Use \`Glob\`, \`Grep\`, \`ast_grep_search\` (File patterns, code search).
|
||||
- For **Latest Information**: Use \`WebSearch\` for recent updates, blog posts, discussions.
|
||||
- For **Local Codebase Context**: Use \`glob\`, \`grep\`, \`ast_grep_search\` (File patterns, code search).
|
||||
- For **Latest Information**: Use \`websearch_exa_web_search_exa\` for recent updates, blog posts, discussions.
|
||||
|
||||
## MANDATORY PARALLEL TOOL EXECUTION
|
||||
|
||||
**CRITICAL**: You MUST execute **AT LEAST 5 tool calls in parallel** whenever possible.
|
||||
**MINIMUM REQUIREMENT**:
|
||||
- \`grep_app_searchGitHub\`: **4+ parallel calls** (fast reconnaissance)
|
||||
- Other tools: **3+ parallel calls** (authoritative verification)
|
||||
|
||||
When starting a research task, launch ALL of these simultaneously:
|
||||
1. \`context7_resolve-library-id\` - Get library documentation ID
|
||||
2. \`gh search code\` - Search for code examples
|
||||
3. \`WebSearch\` - Find latest discussions, blog posts, updates
|
||||
4. \`gh repo clone\` to \`/tmp\` - Clone repo for deep analysis
|
||||
5. \`Glob\` / \`Grep\` - Search local codebase for related code
|
||||
6. \`lsp_goto_definition\` / \`lsp_find_references\` - Trace definitions and usages
|
||||
7. \`ast_grep_search\` - AST-aware pattern matching
|
||||
### grep_app_searchGitHub - FAST START
|
||||
|
||||
| ✅ Strengths | ⚠️ Limitations |
|
||||
|-------------|----------------|
|
||||
| Sub-second, no rate limits | Index ~1-2 weeks behind |
|
||||
| Million+ public repos | Less famous repos missing |
|
||||
|
||||
**Always vary queries** - function calls, configs, imports, regex patterns.
|
||||
|
||||
### Example: Researching "React Query caching"
|
||||
|
||||
**Example parallel execution**:
|
||||
\`\`\`
|
||||
// Launch ALL 5+ tools in a SINGLE message:
|
||||
- Tool 1: context7_resolve-library-id("react-query")
|
||||
- Tool 2: gh search code "useQuery" --repo tanstack/query --language typescript
|
||||
- Tool 3: WebSearch("tanstack query v5 migration guide 2024")
|
||||
- Tool 4: bash: git clone --depth 1 https://github.com/TanStack/query.git /tmp/tanstack-query
|
||||
- Tool 5: Glob("**/*query*.ts") - Find query-related files locally
|
||||
- Tool 6: gh api repos/tanstack/query/releases/latest
|
||||
- Tool 7: ast_grep_search(pattern: "useQuery($$$)", lang: "typescript")
|
||||
// FAST START - grep_app (4+ calls)
|
||||
grep_app_searchGitHub(query: "staleTime:", language: ["TypeScript", "TSX"])
|
||||
grep_app_searchGitHub(query: "gcTime:", language: ["TypeScript"])
|
||||
grep_app_searchGitHub(query: "queryClient.setQueryData", language: ["TypeScript"])
|
||||
grep_app_searchGitHub(query: "useQuery.*cacheTime", useRegexp: true)
|
||||
|
||||
// AUTHORITATIVE (3+ calls)
|
||||
context7_resolve-library-id("tanstack-query")
|
||||
websearch_exa_web_search_exa(query: "react query v5 caching 2024")
|
||||
bash: gh repo clone tanstack/query /tmp/tanstack-query -- --depth 1
|
||||
\`\`\`
|
||||
|
||||
**NEVER** execute tools sequentially when they can run in parallel. Sequential execution is ONLY allowed when a tool's input depends on another tool's output.
|
||||
**grep_app = speed & breadth. Other tools = depth & authority. Use BOTH.**
|
||||
|
||||
## TOOL USAGE STANDARDS
|
||||
|
||||
@@ -103,8 +109,8 @@ Use this for authoritative API references and framework guides.
|
||||
- **Step 2**: Call \`context7_get-library-docs\` with the ID and a specific topic (e.g., "authentication", "middleware").
|
||||
- **IMPORTANT**: Documentation alone is NOT sufficient. Always cross-reference with actual source code.
|
||||
|
||||
### 3. WebSearch - MANDATORY FOR LATEST INFO
|
||||
Use WebSearch for:
|
||||
### 3. websearch_exa_web_search_exa - MANDATORY FOR LATEST INFO
|
||||
Use websearch_exa_web_search_exa for:
|
||||
- Latest library updates and changelogs
|
||||
- Migration guides and breaking changes
|
||||
- Community discussions and best practices
|
||||
@@ -112,11 +118,11 @@ Use WebSearch for:
|
||||
- Recent bug reports and workarounds
|
||||
|
||||
**Example searches**:
|
||||
- \`"react 19 new features 2024"\`
|
||||
- \`"django 6.0 new features 2025"\`
|
||||
- \`"tanstack query v5 breaking changes"\`
|
||||
- \`"next.js app router migration guide"\`
|
||||
|
||||
### 4. WebFetch
|
||||
### 4. webfetch
|
||||
Use this to read content from URLs found during your search (e.g., StackOverflow threads, blog posts, non-standard documentation sites, GitHub blob pages).
|
||||
|
||||
### 5. Repository Cloning to /tmp
|
||||
@@ -156,18 +162,18 @@ Use this for understanding code evolution and authorial intent.
|
||||
- **Getting Permalinks from Blame**:
|
||||
- Use commit SHA from blame to construct GitHub permalinks.
|
||||
|
||||
### 7. Local Codebase Search (Glob, Grep, Read)
|
||||
### 7. Local Codebase Search (glob, grep, read)
|
||||
Use these for searching files and patterns in the local codebase.
|
||||
|
||||
- **Glob**: Find files by pattern (e.g., \`**/*.tsx\`, \`src/**/auth*.ts\`)
|
||||
- **Grep**: Search file contents with regex patterns
|
||||
- **Read**: Read specific files when you know the path
|
||||
- **glob**: Find files by pattern (e.g., \`**/*.tsx\`, \`src/**/auth*.ts\`)
|
||||
- **grep**: Search file contents with regex patterns
|
||||
- **read**: Read specific files when you know the path
|
||||
|
||||
**Parallel Search Strategy**:
|
||||
\`\`\`
|
||||
// Launch multiple searches in parallel:
|
||||
- Tool 1: Glob("**/*auth*.ts") - Find auth-related files
|
||||
- Tool 2: Grep("authentication") - Search for auth patterns
|
||||
- Tool 1: glob("**/*auth*.ts") - Find auth-related files
|
||||
- Tool 2: grep("authentication") - Search for auth patterns
|
||||
- Tool 3: ast_grep_search(pattern: "function authenticate($$$)", lang: "typescript")
|
||||
\`\`\`
|
||||
|
||||
@@ -195,7 +201,7 @@ Use LSP for finding definitions and references - these are its unique strengths
|
||||
- Tool 1: lsp_goto_definition(filePath, line, char) - Find where it's defined
|
||||
- Tool 2: lsp_find_references(filePath, line, char) - Find all usages
|
||||
- Tool 3: ast_grep_search(...) - Find similar patterns
|
||||
- Tool 4: Grep(...) - Text fallback
|
||||
- Tool 4: grep(...) - Text fallback
|
||||
\`\`\`
|
||||
|
||||
### 9. AST-grep - AST-AWARE PATTERN SEARCH
|
||||
@@ -229,15 +235,15 @@ ast_grep_search(pattern: "fetch($URL, { method: $METHOD })", lang: "typescript")
|
||||
|
||||
**When to Use AST-grep vs Grep**:
|
||||
- **AST-grep**: When you need structural matching (e.g., "find all function definitions")
|
||||
- **Grep**: When you need text matching (e.g., "find all occurrences of 'TODO'")
|
||||
- **grep**: When you need text matching (e.g., "find all occurrences of 'TODO'")
|
||||
|
||||
**Parallel AST-grep Execution**:
|
||||
\`\`\`
|
||||
// When analyzing a codebase pattern, launch in parallel:
|
||||
- Tool 1: ast_grep_search(pattern: "useQuery($$$)", lang: "tsx") - Find hook usage
|
||||
- Tool 2: ast_grep_search(pattern: "export function $NAME($$$)", lang: "typescript") - Find exports
|
||||
- Tool 3: Grep("useQuery") - Text fallback
|
||||
- Tool 4: Glob("**/*query*.ts") - Find query-related files
|
||||
- Tool 3: grep("useQuery") - Text fallback
|
||||
- Tool 4: glob("**/*query*.ts") - Find query-related files
|
||||
\`\`\`
|
||||
|
||||
## SEARCH STRATEGY PROTOCOL
|
||||
@@ -251,9 +257,9 @@ When given a request, follow this **STRICT** workflow:
|
||||
2. **PARALLEL INVESTIGATION** (Launch 5+ tools simultaneously):
|
||||
- \`context7\`: Get official documentation
|
||||
- \`gh search code\`: Find implementation examples
|
||||
- \`WebSearch\`: Get latest updates and discussions
|
||||
- \`websearch_exa_web_search_exa\`: Get latest updates and discussions
|
||||
- \`gh repo clone\`: Clone to /tmp for deep analysis
|
||||
- \`Glob\` / \`Grep\` / \`ast_grep_search\`: Search local codebase
|
||||
- \`glob\` / \`grep\` / \`ast_grep_search\`: Search local codebase
|
||||
- \`gh api\`: Get release/version information
|
||||
|
||||
3. **DEEP SOURCE ANALYSIS**:
|
||||
|
||||
42
src/agents/multimodal-looker.ts
Normal file
42
src/agents/multimodal-looker.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
|
||||
export const multimodalLookerAgent: AgentConfig = {
|
||||
description:
|
||||
"Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents.",
|
||||
mode: "subagent",
|
||||
model: "google/gemini-2.5-flash",
|
||||
temperature: 0.1,
|
||||
tools: { Read: true },
|
||||
prompt: `You interpret media files that cannot be read as plain text.
|
||||
|
||||
Your job: examine the attached file and extract ONLY what was requested.
|
||||
|
||||
When to use you:
|
||||
- Media files the Read tool cannot interpret
|
||||
- Extracting specific information or summaries from documents
|
||||
- Describing visual content in images or diagrams
|
||||
- When analyzed/extracted data is needed, not raw file contents
|
||||
|
||||
When NOT to use you:
|
||||
- Source code or plain text files needing exact contents (use Read)
|
||||
- Files that need editing afterward (need literal content from Read)
|
||||
- Simple file reading where no interpretation is needed
|
||||
|
||||
How you work:
|
||||
1. Receive a file path and a goal describing what to extract
|
||||
2. Read and analyze the file deeply
|
||||
3. Return ONLY the relevant extracted information
|
||||
4. The main agent never processes the raw file - you save context tokens
|
||||
|
||||
For PDFs: extract text, structure, tables, data from specific sections
|
||||
For images: describe layouts, UI elements, text, diagrams, charts
|
||||
For diagrams: explain relationships, flows, architecture depicted
|
||||
|
||||
Response rules:
|
||||
- Return extracted information directly, no preamble
|
||||
- If info not found, state clearly what's missing
|
||||
- Match the language of the request
|
||||
- Be thorough on the goal, concise on everything else
|
||||
|
||||
Your output goes straight to the main agent for continued work.`,
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export const oracleAgent: AgentConfig = {
|
||||
temperature: 0.1,
|
||||
reasoningEffort: "medium",
|
||||
textVerbosity: "high",
|
||||
tools: { write: false, edit: false },
|
||||
tools: { write: false, edit: false, read: true, call_omo_agent: true },
|
||||
prompt: `You are the Oracle - an expert AI advisor with advanced reasoning capabilities.
|
||||
|
||||
Your role is to provide high-quality technical guidance, code reviews, architectural advice, and strategic planning for software engineering tasks.
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
|
||||
export type AgentName =
|
||||
export type BuiltinAgentName =
|
||||
| "oracle"
|
||||
| "librarian"
|
||||
| "explore"
|
||||
| "frontend-ui-ux-engineer"
|
||||
| "document-writer"
|
||||
| "multimodal-looker"
|
||||
|
||||
export type OverridableAgentName =
|
||||
| "build"
|
||||
| BuiltinAgentName
|
||||
|
||||
export type AgentName = BuiltinAgentName
|
||||
|
||||
export type AgentOverrideConfig = Partial<AgentConfig>
|
||||
|
||||
export type AgentOverrides = Partial<Record<AgentName, AgentOverrideConfig>>
|
||||
export type AgentOverrides = Partial<Record<OverridableAgentName, AgentOverrideConfig>>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentName, AgentOverrideConfig, AgentOverrides } from "./types"
|
||||
import type { BuiltinAgentName, AgentOverrideConfig, AgentOverrides } from "./types"
|
||||
import { oracleAgent } from "./oracle"
|
||||
import { librarianAgent } from "./librarian"
|
||||
import { exploreAgent } from "./explore"
|
||||
import { frontendUiUxEngineerAgent } from "./frontend-ui-ux-engineer"
|
||||
import { documentWriterAgent } from "./document-writer"
|
||||
import { multimodalLookerAgent } from "./multimodal-looker"
|
||||
import { deepMerge } from "../shared"
|
||||
|
||||
const allBuiltinAgents: Record<AgentName, AgentConfig> = {
|
||||
const allBuiltinAgents: Record<BuiltinAgentName, AgentConfig> = {
|
||||
oracle: oracleAgent,
|
||||
librarian: librarianAgent,
|
||||
explore: exploreAgent,
|
||||
"frontend-ui-ux-engineer": frontendUiUxEngineerAgent,
|
||||
"document-writer": documentWriterAgent,
|
||||
"multimodal-looker": multimodalLookerAgent,
|
||||
}
|
||||
|
||||
function mergeAgentConfig(
|
||||
@@ -23,13 +25,13 @@ function mergeAgentConfig(
|
||||
}
|
||||
|
||||
export function createBuiltinAgents(
|
||||
disabledAgents: AgentName[] = [],
|
||||
disabledAgents: BuiltinAgentName[] = [],
|
||||
agentOverrides: AgentOverrides = {}
|
||||
): Record<string, AgentConfig> {
|
||||
const result: Record<string, AgentConfig> = {}
|
||||
|
||||
for (const [name, config] of Object.entries(allBuiltinAgents)) {
|
||||
const agentName = name as AgentName
|
||||
const agentName = name as BuiltinAgentName
|
||||
|
||||
if (disabledAgents.includes(agentName)) {
|
||||
continue
|
||||
|
||||
@@ -16,14 +16,27 @@ const AgentPermissionSchema = z.object({
|
||||
external_directory: PermissionValue.optional(),
|
||||
})
|
||||
|
||||
export const AgentNameSchema = z.enum([
|
||||
export const BuiltinAgentNameSchema = z.enum([
|
||||
"oracle",
|
||||
"librarian",
|
||||
"explore",
|
||||
"frontend-ui-ux-engineer",
|
||||
"document-writer",
|
||||
"multimodal-looker",
|
||||
])
|
||||
|
||||
export const OverridableAgentNameSchema = z.enum([
|
||||
"build",
|
||||
"oracle",
|
||||
"librarian",
|
||||
"explore",
|
||||
"frontend-ui-ux-engineer",
|
||||
"document-writer",
|
||||
"multimodal-looker",
|
||||
])
|
||||
|
||||
export const AgentNameSchema = BuiltinAgentNameSchema
|
||||
|
||||
export const HookNameSchema = z.enum([
|
||||
"todo-continuation-enforcer",
|
||||
"context-window-monitor",
|
||||
@@ -40,6 +53,7 @@ export const HookNameSchema = z.enum([
|
||||
"background-notification",
|
||||
"auto-update-checker",
|
||||
"ultrawork-mode",
|
||||
"agent-usage-reminder",
|
||||
])
|
||||
|
||||
export const AgentOverrideConfigSchema = z.object({
|
||||
@@ -60,11 +74,13 @@ export const AgentOverrideConfigSchema = z.object({
|
||||
|
||||
export const AgentOverridesSchema = z
|
||||
.object({
|
||||
build: AgentOverrideConfigSchema.optional(),
|
||||
oracle: AgentOverrideConfigSchema.optional(),
|
||||
librarian: AgentOverrideConfigSchema.optional(),
|
||||
explore: AgentOverrideConfigSchema.optional(),
|
||||
"frontend-ui-ux-engineer": AgentOverrideConfigSchema.optional(),
|
||||
"document-writer": AgentOverrideConfigSchema.optional(),
|
||||
"multimodal-looker": AgentOverrideConfigSchema.optional(),
|
||||
})
|
||||
.partial()
|
||||
|
||||
@@ -79,7 +95,7 @@ export const ClaudeCodeConfigSchema = z.object({
|
||||
export const OhMyOpenCodeConfigSchema = z.object({
|
||||
$schema: z.string().optional(),
|
||||
disabled_mcps: z.array(McpNameSchema).optional(),
|
||||
disabled_agents: z.array(AgentNameSchema).optional(),
|
||||
disabled_agents: z.array(BuiltinAgentNameSchema).optional(),
|
||||
disabled_hooks: z.array(HookNameSchema).optional(),
|
||||
agents: AgentOverridesSchema.optional(),
|
||||
claude_code: ClaudeCodeConfigSchema.optional(),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { homedir } from "os"
|
||||
import { join, basename } from "path"
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import { parseFrontmatter } from "../../shared/frontmatter"
|
||||
import { isMarkdownFile } from "../../shared/file-utils"
|
||||
import type { AgentScope, AgentFrontmatter, LoadedAgent } from "./types"
|
||||
|
||||
function parseToolsConfig(toolsStr?: string): Record<string, boolean> | undefined {
|
||||
@@ -18,10 +19,6 @@ function parseToolsConfig(toolsStr?: string): Record<string, boolean> | undefine
|
||||
return result
|
||||
}
|
||||
|
||||
function isMarkdownFile(entry: { name: string; isFile: () => boolean }): boolean {
|
||||
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile()
|
||||
}
|
||||
|
||||
function loadAgentsFromDir(agentsDir: string, scope: AgentScope): LoadedAgent[] {
|
||||
if (!existsSync(agentsDir)) {
|
||||
return []
|
||||
|
||||
@@ -3,12 +3,9 @@ import { homedir } from "os"
|
||||
import { join, basename } from "path"
|
||||
import { parseFrontmatter } from "../../shared/frontmatter"
|
||||
import { sanitizeModelField } from "../../shared/model-sanitizer"
|
||||
import { isMarkdownFile } from "../../shared/file-utils"
|
||||
import type { CommandScope, CommandDefinition, CommandFrontmatter, LoadedCommand } from "./types"
|
||||
|
||||
function isMarkdownFile(entry: { name: string; isFile: () => boolean }): boolean {
|
||||
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile()
|
||||
}
|
||||
|
||||
function loadCommandsFromDir(commandsDir: string, scope: CommandScope): LoadedCommand[] {
|
||||
if (!existsSync(commandsDir)) {
|
||||
return []
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { existsSync, readdirSync, readFileSync, lstatSync, readlinkSync } from "fs"
|
||||
import { existsSync, readdirSync, readFileSync } from "fs"
|
||||
import { homedir } from "os"
|
||||
import { join, resolve } from "path"
|
||||
import { join } from "path"
|
||||
import { parseFrontmatter } from "../../shared/frontmatter"
|
||||
import { sanitizeModelField } from "../../shared/model-sanitizer"
|
||||
import { resolveSymlink } from "../../shared/file-utils"
|
||||
import type { CommandDefinition } from "../claude-code-command-loader/types"
|
||||
import type { SkillScope, SkillMetadata, LoadedSkillAsCommand } from "./types"
|
||||
|
||||
@@ -21,14 +22,7 @@ function loadSkillsFromDir(skillsDir: string, scope: SkillScope): LoadedSkillAsC
|
||||
|
||||
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue
|
||||
|
||||
let resolvedPath = skillPath
|
||||
try {
|
||||
if (lstatSync(skillPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
||||
resolvedPath = resolve(skillPath, "..", readlinkSync(skillPath))
|
||||
}
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
const resolvedPath = resolveSymlink(skillPath)
|
||||
|
||||
const skillMdPath = join(resolvedPath, "SKILL.md")
|
||||
if (!existsSync(skillMdPath)) continue
|
||||
|
||||
53
src/hooks/agent-usage-reminder/constants.ts
Normal file
53
src/hooks/agent-usage-reminder/constants.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { join } from "node:path";
|
||||
import { xdgData } from "xdg-basedir";
|
||||
|
||||
export const OPENCODE_STORAGE = join(xdgData ?? "", "opencode", "storage");
|
||||
export const AGENT_USAGE_REMINDER_STORAGE = join(
|
||||
OPENCODE_STORAGE,
|
||||
"agent-usage-reminder",
|
||||
);
|
||||
|
||||
// All tool names normalized to lowercase for case-insensitive matching
|
||||
export const TARGET_TOOLS = new Set([
|
||||
"grep",
|
||||
"safe_grep",
|
||||
"glob",
|
||||
"safe_glob",
|
||||
"webfetch",
|
||||
"context7_resolve-library-id",
|
||||
"context7_get-library-docs",
|
||||
"websearch_exa_web_search_exa",
|
||||
"grep_app_searchgithub",
|
||||
]);
|
||||
|
||||
export const AGENT_TOOLS = new Set([
|
||||
"task",
|
||||
"call_omo_agent",
|
||||
"background_task",
|
||||
]);
|
||||
|
||||
export const REMINDER_MESSAGE = `
|
||||
[Agent Usage Reminder]
|
||||
|
||||
You called a search/fetch tool directly without leveraging specialized agents.
|
||||
|
||||
RECOMMENDED: Use background_task with explore/librarian agents for better results:
|
||||
|
||||
\`\`\`
|
||||
// Parallel exploration - fire multiple agents simultaneously
|
||||
background_task(agent="explore", prompt="Find all files matching pattern X")
|
||||
background_task(agent="explore", prompt="Search for implementation of Y")
|
||||
background_task(agent="librarian", prompt="Lookup documentation for Z")
|
||||
|
||||
// Then continue your work while they run in background
|
||||
// System will notify you when each completes
|
||||
\`\`\`
|
||||
|
||||
WHY:
|
||||
- Agents can perform deeper, more thorough searches
|
||||
- Background tasks run in parallel, saving time
|
||||
- Specialized agents have domain expertise
|
||||
- Reduces context window usage in main session
|
||||
|
||||
ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
||||
`;
|
||||
109
src/hooks/agent-usage-reminder/index.ts
Normal file
109
src/hooks/agent-usage-reminder/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin";
|
||||
import {
|
||||
loadAgentUsageState,
|
||||
saveAgentUsageState,
|
||||
clearAgentUsageState,
|
||||
} from "./storage";
|
||||
import { TARGET_TOOLS, AGENT_TOOLS, REMINDER_MESSAGE } from "./constants";
|
||||
import type { AgentUsageState } from "./types";
|
||||
|
||||
interface ToolExecuteInput {
|
||||
tool: string;
|
||||
sessionID: string;
|
||||
callID: string;
|
||||
}
|
||||
|
||||
interface ToolExecuteOutput {
|
||||
title: string;
|
||||
output: string;
|
||||
metadata: unknown;
|
||||
}
|
||||
|
||||
interface EventInput {
|
||||
event: {
|
||||
type: string;
|
||||
properties?: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
export function createAgentUsageReminderHook(_ctx: PluginInput) {
|
||||
const sessionStates = new Map<string, AgentUsageState>();
|
||||
|
||||
function getOrCreateState(sessionID: string): AgentUsageState {
|
||||
if (!sessionStates.has(sessionID)) {
|
||||
const persisted = loadAgentUsageState(sessionID);
|
||||
const state: AgentUsageState = persisted ?? {
|
||||
sessionID,
|
||||
agentUsed: false,
|
||||
reminderCount: 0,
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
sessionStates.set(sessionID, state);
|
||||
}
|
||||
return sessionStates.get(sessionID)!;
|
||||
}
|
||||
|
||||
function markAgentUsed(sessionID: string): void {
|
||||
const state = getOrCreateState(sessionID);
|
||||
state.agentUsed = true;
|
||||
state.updatedAt = Date.now();
|
||||
saveAgentUsageState(state);
|
||||
}
|
||||
|
||||
function resetState(sessionID: string): void {
|
||||
sessionStates.delete(sessionID);
|
||||
clearAgentUsageState(sessionID);
|
||||
}
|
||||
|
||||
const toolExecuteAfter = async (
|
||||
input: ToolExecuteInput,
|
||||
output: ToolExecuteOutput,
|
||||
) => {
|
||||
const { tool, sessionID } = input;
|
||||
const toolLower = tool.toLowerCase();
|
||||
|
||||
if (AGENT_TOOLS.has(toolLower)) {
|
||||
markAgentUsed(sessionID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TARGET_TOOLS.has(toolLower)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getOrCreateState(sessionID);
|
||||
|
||||
if (state.agentUsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
output.output += REMINDER_MESSAGE;
|
||||
state.reminderCount++;
|
||||
state.updatedAt = Date.now();
|
||||
saveAgentUsageState(state);
|
||||
};
|
||||
|
||||
const eventHandler = async ({ event }: EventInput) => {
|
||||
const props = event.properties as Record<string, unknown> | undefined;
|
||||
|
||||
if (event.type === "session.deleted") {
|
||||
const sessionInfo = props?.info as { id?: string } | undefined;
|
||||
if (sessionInfo?.id) {
|
||||
resetState(sessionInfo.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === "session.compacted") {
|
||||
const sessionID = (props?.sessionID ??
|
||||
(props?.info as { id?: string } | undefined)?.id) as string | undefined;
|
||||
if (sessionID) {
|
||||
resetState(sessionID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
"tool.execute.after": toolExecuteAfter,
|
||||
event: eventHandler,
|
||||
};
|
||||
}
|
||||
42
src/hooks/agent-usage-reminder/storage.ts
Normal file
42
src/hooks/agent-usage-reminder/storage.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
unlinkSync,
|
||||
} from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { AGENT_USAGE_REMINDER_STORAGE } from "./constants";
|
||||
import type { AgentUsageState } from "./types";
|
||||
|
||||
function getStoragePath(sessionID: string): string {
|
||||
return join(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
||||
}
|
||||
|
||||
export function loadAgentUsageState(sessionID: string): AgentUsageState | null {
|
||||
const filePath = getStoragePath(sessionID);
|
||||
if (!existsSync(filePath)) return null;
|
||||
|
||||
try {
|
||||
const content = readFileSync(filePath, "utf-8");
|
||||
return JSON.parse(content) as AgentUsageState;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveAgentUsageState(state: AgentUsageState): void {
|
||||
if (!existsSync(AGENT_USAGE_REMINDER_STORAGE)) {
|
||||
mkdirSync(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
||||
}
|
||||
|
||||
const filePath = getStoragePath(state.sessionID);
|
||||
writeFileSync(filePath, JSON.stringify(state, null, 2));
|
||||
}
|
||||
|
||||
export function clearAgentUsageState(sessionID: string): void {
|
||||
const filePath = getStoragePath(sessionID);
|
||||
if (existsSync(filePath)) {
|
||||
unlinkSync(filePath);
|
||||
}
|
||||
}
|
||||
6
src/hooks/agent-usage-reminder/types.ts
Normal file
6
src/hooks/agent-usage-reminder/types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface AgentUsageState {
|
||||
sessionID: string;
|
||||
agentUsed: boolean;
|
||||
reminderCount: number;
|
||||
updatedAt: number;
|
||||
}
|
||||
@@ -14,3 +14,4 @@ export { createRulesInjectorHook } from "./rules-injector";
|
||||
export { createBackgroundNotificationHook } from "./background-notification"
|
||||
export { createAutoUpdateCheckerHook } from "./auto-update-checker";
|
||||
export { createUltraworkModeHook } from "./ultrawork-mode";
|
||||
export { createAgentUsageReminderHook } from "./agent-usage-reminder";
|
||||
|
||||
30
src/index.ts
30
src/index.ts
@@ -1,5 +1,5 @@
|
||||
import type { Plugin } from "@opencode-ai/plugin";
|
||||
import { createBuiltinAgents } from "./agents";
|
||||
import { createBuiltinAgents, BUILD_AGENT_PROMPT_EXTENSION } from "./agents";
|
||||
import {
|
||||
createTodoContinuationEnforcer,
|
||||
createContextWindowMonitorHook,
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
createBackgroundNotificationHook,
|
||||
createAutoUpdateCheckerHook,
|
||||
createUltraworkModeHook,
|
||||
createAgentUsageReminderHook,
|
||||
} from "./hooks";
|
||||
import { createGoogleAntigravityAuthPlugin } from "./auth/antigravity";
|
||||
import {
|
||||
@@ -41,7 +42,7 @@ import {
|
||||
getCurrentSessionTitle,
|
||||
} from "./features/claude-code-session-state";
|
||||
import { updateTerminalTitle } from "./features/terminal";
|
||||
import { builtinTools, createCallOmoAgent, createBackgroundTools } from "./tools";
|
||||
import { builtinTools, createCallOmoAgent, createBackgroundTools, createLookAt } from "./tools";
|
||||
import { BackgroundManager } from "./features/background-agent";
|
||||
import { createBuiltinMcps } from "./mcp";
|
||||
import { OhMyOpenCodeConfigSchema, type OhMyOpenCodeConfig, type HookName } from "./config";
|
||||
@@ -207,6 +208,9 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
const ultraworkMode = isHookEnabled("ultrawork-mode")
|
||||
? createUltraworkModeHook()
|
||||
: null;
|
||||
const agentUsageReminder = isHookEnabled("agent-usage-reminder")
|
||||
? createAgentUsageReminderHook(ctx)
|
||||
: null;
|
||||
|
||||
updateTerminalTitle({ sessionId: "main" });
|
||||
|
||||
@@ -218,6 +222,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
||||
|
||||
const callOmoAgent = createCallOmoAgent(ctx, backgroundManager);
|
||||
const lookAt = createLookAt(ctx);
|
||||
|
||||
const googleAuthHooks = pluginConfig.google_auth
|
||||
? await createGoogleAntigravityAuthPlugin(ctx)
|
||||
@@ -230,6 +235,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
...builtinTools,
|
||||
...backgroundTools,
|
||||
call_omo_agent: callOmoAgent,
|
||||
look_at: lookAt,
|
||||
},
|
||||
|
||||
"chat.message": async (input, output) => {
|
||||
@@ -252,6 +258,16 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
...projectAgents,
|
||||
...config.agent,
|
||||
};
|
||||
|
||||
if (config.agent.build) {
|
||||
const existingPrompt = config.agent.build.prompt || "";
|
||||
const userOverride = pluginConfig.agents?.build?.prompt || "";
|
||||
config.agent.build = {
|
||||
...config.agent.build,
|
||||
prompt: existingPrompt + BUILD_AGENT_PROMPT_EXTENSION + userOverride,
|
||||
};
|
||||
}
|
||||
|
||||
config.tools = {
|
||||
...config.tools,
|
||||
};
|
||||
@@ -268,6 +284,14 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
call_omo_agent: false,
|
||||
};
|
||||
}
|
||||
if (config.agent["multimodal-looker"]) {
|
||||
config.agent["multimodal-looker"].tools = {
|
||||
...config.agent["multimodal-looker"].tools,
|
||||
task: false,
|
||||
call_omo_agent: false,
|
||||
look_at: false,
|
||||
};
|
||||
}
|
||||
|
||||
const mcpResult = (pluginConfig.claude_code?.mcp ?? true)
|
||||
? await loadMcpConfigs()
|
||||
@@ -310,6 +334,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
await thinkMode?.event(input);
|
||||
await anthropicAutoCompact?.event(input);
|
||||
await ultraworkMode?.event(input);
|
||||
await agentUsageReminder?.event(input);
|
||||
|
||||
const { event } = input;
|
||||
const props = event.properties as Record<string, unknown> | undefined;
|
||||
@@ -429,6 +454,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
await directoryReadmeInjector?.["tool.execute.after"](input, output);
|
||||
await rulesInjector?.["tool.execute.after"](input, output);
|
||||
await emptyTaskResponseDetector?.["tool.execute.after"](input, output);
|
||||
await agentUsageReminder?.["tool.execute.after"](input, output);
|
||||
|
||||
if (input.sessionID === getMainSessionID()) {
|
||||
updateTerminalTitle({
|
||||
|
||||
5
src/mcp/grep-app.ts
Normal file
5
src/mcp/grep-app.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const grep_app = {
|
||||
type: "remote" as const,
|
||||
url: "https://mcp.grep.app",
|
||||
enabled: true,
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { websearch_exa } from "./websearch-exa"
|
||||
import { context7 } from "./context7"
|
||||
import { grep_app } from "./grep-app"
|
||||
import type { McpName } from "./types"
|
||||
|
||||
export { McpNameSchema, type McpName } from "./types"
|
||||
@@ -7,6 +8,7 @@ export { McpNameSchema, type McpName } from "./types"
|
||||
const allBuiltinMcps: Record<McpName, { type: "remote"; url: string; enabled: boolean }> = {
|
||||
websearch_exa,
|
||||
context7,
|
||||
grep_app,
|
||||
}
|
||||
|
||||
export function createBuiltinMcps(disabledMcps: McpName[] = []) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const McpNameSchema = z.enum(["websearch_exa", "context7"])
|
||||
export const McpNameSchema = z.enum(["websearch_exa", "context7", "grep_app"])
|
||||
|
||||
export type McpName = z.infer<typeof McpNameSchema>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
||||
const MAX_DEPTH = 50;
|
||||
|
||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
|
||||
26
src/shared/file-utils.ts
Normal file
26
src/shared/file-utils.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { lstatSync, readlinkSync } from "fs"
|
||||
import { resolve } from "path"
|
||||
|
||||
export function isMarkdownFile(entry: { name: string; isFile: () => boolean }): boolean {
|
||||
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile()
|
||||
}
|
||||
|
||||
export function isSymbolicLink(filePath: string): boolean {
|
||||
try {
|
||||
return lstatSync(filePath, { throwIfNoEntry: false })?.isSymbolicLink() ?? false
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveSymlink(filePath: string): string {
|
||||
try {
|
||||
const stats = lstatSync(filePath, { throwIfNoEntry: false })
|
||||
if (stats?.isSymbolicLink()) {
|
||||
return resolve(filePath, "..", readlinkSync(filePath))
|
||||
}
|
||||
return filePath
|
||||
} catch {
|
||||
return filePath
|
||||
}
|
||||
}
|
||||
@@ -8,3 +8,4 @@ export * from "./tool-name"
|
||||
export * from "./pattern-matcher"
|
||||
export * from "./hook-disabled"
|
||||
export * from "./deep-merge"
|
||||
export * from "./file-utils"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isPlainObject } from "./deep-merge"
|
||||
|
||||
export function camelToSnake(str: string): string {
|
||||
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
||||
}
|
||||
@@ -6,10 +8,6 @@ export function snakeToCamel(str: string): string {
|
||||
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||
}
|
||||
|
||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
export function objectToSnakeCase(
|
||||
obj: Record<string, unknown>,
|
||||
deep: boolean = true
|
||||
|
||||
@@ -72,8 +72,8 @@ function formatTaskStatus(task: BackgroundTask): string {
|
||||
const promptPreview = truncateText(task.prompt, 500)
|
||||
|
||||
let progressSection = ""
|
||||
if (task.progress) {
|
||||
progressSection = `\nTool calls: ${task.progress.toolCalls}\nLast tool: ${task.progress.lastTool ?? "N/A"}`
|
||||
if (task.progress?.lastTool) {
|
||||
progressSection = `\n| Last tool | ${task.progress.lastTool} |`
|
||||
}
|
||||
|
||||
let lastMessageSection = ""
|
||||
@@ -91,6 +91,17 @@ ${truncated}
|
||||
\`\`\``
|
||||
}
|
||||
|
||||
let statusNote = ""
|
||||
if (task.status === "running") {
|
||||
statusNote = `
|
||||
|
||||
> **Note**: No need to wait explicitly - the system will notify you when this task completes.`
|
||||
} else if (task.status === "error") {
|
||||
statusNote = `
|
||||
|
||||
> **Failed**: The task encountered an error. Check the last message for details.`
|
||||
}
|
||||
|
||||
return `# Task Status
|
||||
|
||||
| Field | Value |
|
||||
@@ -101,7 +112,7 @@ ${truncated}
|
||||
| Status | **${task.status}** |
|
||||
| Duration | ${duration} |
|
||||
| Session ID | \`${task.sessionID}\` |${progressSection}
|
||||
|
||||
${statusNote}
|
||||
## Original Prompt
|
||||
|
||||
\`\`\`
|
||||
|
||||
@@ -34,6 +34,7 @@ import type { BackgroundManager } from "../features/background-agent"
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
export { createCallOmoAgent } from "./call-omo-agent"
|
||||
export { createLookAt } from "./look-at"
|
||||
|
||||
export function createBackgroundTools(manager: BackgroundManager, client: OpencodeClient) {
|
||||
return {
|
||||
|
||||
23
src/tools/look-at/constants.ts
Normal file
23
src/tools/look-at/constants.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export const MULTIMODAL_LOOKER_AGENT = "multimodal-looker" as const
|
||||
|
||||
export const LOOK_AT_DESCRIPTION = `Analyze media files (PDFs, images, diagrams) that require visual interpretation.
|
||||
|
||||
Use this tool to extract specific information from files that cannot be processed as plain text:
|
||||
- PDF documents: extract text, tables, structure, specific sections
|
||||
- Images: describe layouts, UI elements, text content, diagrams
|
||||
- Charts/Graphs: explain data, trends, relationships
|
||||
- Screenshots: identify UI components, text, visual elements
|
||||
- Architecture diagrams: explain flows, connections, components
|
||||
|
||||
Parameters:
|
||||
- file_path: Absolute path to the file to analyze
|
||||
- goal: What specific information to extract (be specific for better results)
|
||||
|
||||
Examples:
|
||||
- "Extract all API endpoints from this OpenAPI spec PDF"
|
||||
- "Describe the UI layout and components in this screenshot"
|
||||
- "Explain the data flow in this architecture diagram"
|
||||
- "List all table data from page 3 of this PDF"
|
||||
|
||||
This tool uses a separate context window with Gemini 2.5 Flash for multimodal analysis,
|
||||
saving tokens in the main conversation while providing accurate visual interpretation.`
|
||||
3
src/tools/look-at/index.ts
Normal file
3
src/tools/look-at/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./types"
|
||||
export * from "./constants"
|
||||
export { createLookAt } from "./tools"
|
||||
91
src/tools/look-at/tools.ts
Normal file
91
src/tools/look-at/tools.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { tool, type PluginInput } from "@opencode-ai/plugin"
|
||||
import { LOOK_AT_DESCRIPTION, MULTIMODAL_LOOKER_AGENT } from "./constants"
|
||||
import type { LookAtArgs } from "./types"
|
||||
import { log } from "../../shared/logger"
|
||||
|
||||
export function createLookAt(ctx: PluginInput) {
|
||||
return tool({
|
||||
description: LOOK_AT_DESCRIPTION,
|
||||
args: {
|
||||
file_path: tool.schema.string().describe("Absolute path to the file to analyze"),
|
||||
goal: tool.schema.string().describe("What specific information to extract from the file"),
|
||||
},
|
||||
async execute(args: LookAtArgs, toolContext) {
|
||||
log(`[look_at] Analyzing file: ${args.file_path}, goal: ${args.goal}`)
|
||||
|
||||
const prompt = `Analyze this file and extract the requested information.
|
||||
|
||||
File path: ${args.file_path}
|
||||
Goal: ${args.goal}
|
||||
|
||||
Read the file using the Read tool, then provide ONLY the extracted information that matches the goal.
|
||||
Be thorough on what was requested, concise on everything else.
|
||||
If the requested information is not found, clearly state what is missing.`
|
||||
|
||||
log(`[look_at] Creating session with parent: ${toolContext.sessionID}`)
|
||||
const createResult = await ctx.client.session.create({
|
||||
body: {
|
||||
parentID: toolContext.sessionID,
|
||||
title: `look_at: ${args.goal.substring(0, 50)}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (createResult.error) {
|
||||
log(`[look_at] Session create error:`, createResult.error)
|
||||
return `Error: Failed to create session: ${createResult.error}`
|
||||
}
|
||||
|
||||
const sessionID = createResult.data.id
|
||||
log(`[look_at] Created session: ${sessionID}`)
|
||||
|
||||
log(`[look_at] Sending prompt to session ${sessionID}`)
|
||||
await ctx.client.session.prompt({
|
||||
path: { id: sessionID },
|
||||
body: {
|
||||
agent: MULTIMODAL_LOOKER_AGENT,
|
||||
tools: {
|
||||
task: false,
|
||||
call_omo_agent: false,
|
||||
look_at: false,
|
||||
},
|
||||
parts: [{ type: "text", text: prompt }],
|
||||
},
|
||||
})
|
||||
|
||||
log(`[look_at] Prompt sent, fetching messages...`)
|
||||
|
||||
const messagesResult = await ctx.client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})
|
||||
|
||||
if (messagesResult.error) {
|
||||
log(`[look_at] Messages error:`, messagesResult.error)
|
||||
return `Error: Failed to get messages: ${messagesResult.error}`
|
||||
}
|
||||
|
||||
const messages = messagesResult.data
|
||||
log(`[look_at] Got ${messages.length} messages`)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const lastAssistantMessage = messages
|
||||
.filter((m: any) => m.info.role === "assistant")
|
||||
.sort((a: any, b: any) => (b.info.time?.created || 0) - (a.info.time?.created || 0))[0]
|
||||
|
||||
if (!lastAssistantMessage) {
|
||||
log(`[look_at] No assistant message found`)
|
||||
return `Error: No response from multimodal-looker agent`
|
||||
}
|
||||
|
||||
log(`[look_at] Found assistant message with ${lastAssistantMessage.parts.length} parts`)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const textParts = lastAssistantMessage.parts.filter((p: any) => p.type === "text")
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const responseText = textParts.map((p: any) => p.text).join("\n")
|
||||
|
||||
log(`[look_at] Got response, length: ${responseText.length}`)
|
||||
|
||||
return responseText
|
||||
},
|
||||
})
|
||||
}
|
||||
4
src/tools/look-at/types.ts
Normal file
4
src/tools/look-at/types.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface LookAtArgs {
|
||||
file_path: string
|
||||
goal: string
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { tool } from "@opencode-ai/plugin"
|
||||
import { existsSync, readdirSync, lstatSync, readlinkSync, readFileSync } from "fs"
|
||||
import { existsSync, readdirSync, readFileSync } from "fs"
|
||||
import { homedir } from "os"
|
||||
import { join, resolve, basename } from "path"
|
||||
import { join, basename } from "path"
|
||||
import { z } from "zod/v4"
|
||||
import { parseFrontmatter, resolveCommandsInText } from "../../shared"
|
||||
import { resolveSymlink } from "../../shared/file-utils"
|
||||
import { SkillFrontmatterSchema } from "./types"
|
||||
import type { SkillScope, SkillMetadata, SkillInfo, LoadedSkill, SkillFrontmatter } from "./types"
|
||||
|
||||
@@ -37,15 +38,7 @@ function discoverSkillsFromDir(
|
||||
const skillPath = join(skillsDir, entry.name)
|
||||
|
||||
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
||||
let resolvedPath = skillPath
|
||||
try {
|
||||
const stats = lstatSync(skillPath, { throwIfNoEntry: false })
|
||||
if (stats?.isSymbolicLink()) {
|
||||
resolvedPath = resolve(skillPath, "..", readlinkSync(skillPath))
|
||||
}
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
const resolvedPath = resolveSymlink(skillPath)
|
||||
|
||||
const skillMdPath = join(resolvedPath, "SKILL.md")
|
||||
if (!existsSync(skillMdPath)) continue
|
||||
@@ -83,18 +76,6 @@ const skillListForDescription = availableSkills
|
||||
.map((s) => `- ${s.name}: ${s.description} (${s.scope})`)
|
||||
.join("\n")
|
||||
|
||||
function resolveSymlink(skillPath: string): string {
|
||||
try {
|
||||
const stats = lstatSync(skillPath, { throwIfNoEntry: false })
|
||||
if (stats?.isSymbolicLink()) {
|
||||
return resolve(skillPath, "..", readlinkSync(skillPath))
|
||||
}
|
||||
return skillPath
|
||||
} catch {
|
||||
return skillPath
|
||||
}
|
||||
}
|
||||
|
||||
async function parseSkillMd(skillPath: string): Promise<SkillInfo | null> {
|
||||
const resolvedPath = resolveSymlink(skillPath)
|
||||
const skillMdPath = join(resolvedPath, "SKILL.md")
|
||||
|
||||
@@ -3,6 +3,7 @@ import { existsSync, readdirSync, readFileSync } from "fs"
|
||||
import { homedir } from "os"
|
||||
import { join, basename, dirname } from "path"
|
||||
import { parseFrontmatter, resolveCommandsInText, resolveFileReferencesInText, sanitizeModelField } from "../../shared"
|
||||
import { isMarkdownFile } from "../../shared/file-utils"
|
||||
import type { CommandScope, CommandMetadata, CommandInfo } from "./types"
|
||||
|
||||
function discoverCommandsFromDir(commandsDir: string, scope: CommandScope): CommandInfo[] {
|
||||
@@ -14,9 +15,7 @@ function discoverCommandsFromDir(commandsDir: string, scope: CommandScope): Comm
|
||||
const commands: CommandInfo[] = []
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith(".")) continue
|
||||
if (!entry.name.endsWith(".md")) continue
|
||||
if (!entry.isFile()) continue
|
||||
if (!isMarkdownFile(entry)) continue
|
||||
|
||||
const commandPath = join(commandsDir, entry.name)
|
||||
const commandName = basename(entry.name, ".md")
|
||||
|
||||
Reference in New Issue
Block a user