관련노트
히스토리
- 에이전트에게 부탁
agent-shell + OpenCode 커스텀 가이드
개요
Emacs의 agent-shell에서 OpenCode를 ACP(Agent Client Protocol) 프로토콜로 직접 연결하여 사용하는 환경입니다. 기존 Zed의 claude-code-acp wrapper와 달리, OpenCode의 모든 기능을 활용할 수 있습니다.
🎯 프로젝트 목표
agent-shell을 커스터마이징하여 OpenCode의 핵심 강점을 최대한 활용:
- LSP 기능 통합
- 서브에이전트 시스템 활용
- 다중 모델 지원
- MCP 서버 생태계 연동
- 권한 관리 최적화
📊 현재 환경 구조
연결 구조
``` Emacs (agent-shell) └─ ACP Protocol (direct) └─ OpenCode Agent ├─ Model: Claude 3.7 Sonnet (기본) ├─ 서브에이전트: PM, code-refactor, code-reviewer, security-forensics └─ MCP Servers: GitHub, Google Workspace, Jira, Playwright 등 ```
Zed 방식과의 차이
| 항목 | Zed (claude-code-acp) | Emacs (agent-shell + OpenCode) |
|---|---|---|
| 연결 방식 | Wrapper → Claude API | ACP → OpenCode → 다중 모델 |
| 모델 선택 | Claude만 | Anthropic, OpenRouter, DeepSeek 등 |
| 서브에이전트 | ❌ | ✅ PM, refactor, reviewer 등 |
| MCP 서버 | 제한적 | 전체 접근 |
| 권한 처리 | Zed UI | 세션 레벨 공유 |
| LSP 통합 | ❌ | ✅ (개발 예정) |
🔧 OpenCode 아키텍처
1. ACP 에이전트 (acp/agent.ts)
핵심 메서드
```typescript // 세션 초기화 async initialize(params: InitializeRequest): Promise<InitializeResponse>
// 모델 변경 async setSessionModel(params: SetSessionModelRequest)
// 모드(에이전트) 변경 async setSessionMode(params: SetSessionModeRequest)
// 프롬프트 처리 async prompt(params: PromptRequest) ```
이벤트 구독 (setupEventSubscriptions)
```typescript this.config.sdk.event.subscribe({ query: { directory } }).then(async (events) => { for await (const event of events.stream) { switch (event.type) { case “permission.updated”: / 권한 요청 case “message.part.updated”: / 메시지 부분 업데이트 (텍스트, 추론, 도구 호출) } } }) ```
2. 권한 시스템 (permission/index.ts)
세션 레벨 권한 관리
```typescript export async function ask(input: { type: string title: string pattern?: string | string[] sessionID: string messageID: string metadata: Record<string, any> }) { // 1. 이미 승인된 권한 확인 if (covered(keys, approvedForSession)) return
// 2. Plugin 트리거 (설정 파일 기반) switch (await Plugin.trigger(“permission.ask”, info)) { case “deny”: throw new RejectedError() case “allow”: return }
// 3. 사용자 응답 대기 return new Promise<void>((resolve, reject) => { pending[sessionID][info.id] = { info, resolve, reject } Bus.publish(Event.Updated, info) }) } ```
응답 처리
```typescript export function respond(input: { sessionID: string permissionID: string response: “once” | “always” | “reject” }) { if (response = “always”) { approved[sessionID][pattern] = true // 세션 전체에 적용 } } ```
핵심: ACP 모드에서 `always`를 선택하면, 서브에이전트를 포함한 모든 도구 호출이 세션 동안 자동 승인됨!
3. 서브에이전트 시스템 (agent/agent.ts)
내장 에이전트
- `general`: 범용 에이전트 (복잡한 검색, 다단계 작업)
- `build`: 기본 빌드/개발 에이전트
- `plan`: 계획 전용 (읽기 전용 bash, edit 불가)
커스텀 에이전트 설정 (opencode.jsonc)
```jsonc { “agent”: { “PM”: { “description”: “PM 에이전트 역할”, “mode”: “subagent”, “model”: “openrouter/openai/gpt-5.1-chat”, “prompt”: “PM 에이전트 프롬프트…”, “tools”: { “write”: true, “edit”: false } }, “code-refactor”: { “mode”: “subagent”, “model”: “openrouter/openai/gpt-5.1-codex”, “prompt”: “You are a code refactoring expert…”, “tools”: { “write”: true, “edit”: true, “bash”: true } } } } ```
권한 설정
```typescript const defaultPermission: Agent.Info[“permission”] = { edit: “allow”, bash: { ”*”: “allow” }, webfetch: “allow”, doom_loop: “ask”, external_directory: “ask” }
// plan 모드는 읽기 전용 const planPermission = { edit: “deny”, bash: { “ls*”: “allow”, “git status*”: “allow”, “find ”: “allow”, "": “ask” } } ```
4. 프로바이더 시스템 (provider/provider.ts)
현재 사용 가능한 프로바이더
- `anthropic`: Claude 시리즈
- `openrouter`: 다양한 모델 프록시
- `deepseek`: DeepSeek 모델
- `opencode`: OpenCode 자체 모델
모델 ID 형식
``` {providerID}/{modelID}
예시:
- anthropic/claude-3-7-sonnet-20250219
- openrouter/openai/gpt-5.1-chat
- deepseek/deepseek-coder
```
커스텀 로더 예시
```typescript const CUSTOM_LOADERS: Record<string, CustomLoader> = { async anthropic() { return { autoload: false, options: { headers: { “anthropic-beta”: “claude-code-20250219,interleaved-thinking-2025-05-14” } } } } } ```
🎨 커스터마이징 전략
1. LSP 통합 계획
목표
- Emacs LSP 서버와 OpenCode 연동
- 코드 네비게이션, 자동완성, 타입 정보를 AI에게 제공
- 에이전트가 LSP 정보를 활용하여 더 정확한 코드 수정
구현 방향
-
MCP 서버로 LSP 래퍼 개발 ```typescript // mcp-lsp-server { “name”: “lsp”, “tools”: [ “lsp_find_definition”, “lsp_find_references”, “lsp_get_hover”, “lsp_get_completion” ] } ```
-
agent-shell에서 LSP 컨텍스트 전달 ```elisp (defun agent-shell-send-with-lsp-context (prompt) (let ((lsp-info (get-current-lsp-context))) (agent-shell-send-prompt (format “%s\n\nLSP Context:\n%s” prompt lsp-info)))) ```
-
OpenCode 에이전트에 LSP 도구 추가 ```jsonc { “agent”: { “build”: { “tools”: { “lsp_find_definition”: true, “lsp_find_references”: true } } } } ```
2. 서브에이전트 최적화
작업별 모델 할당
```jsonc { “agent”: { “code-refactor”: { “model”: “deepseek/deepseek-coder”, / 코드 리팩토링에 특화 “temperature”: 0.2 }, “PM”: { “model”: “openrouter/openai/gpt-5.1”, / 기획/관리 “temperature”: 0.7 }, “code-reviewer”: { “model”: “anthropic/claude-sonnet-4-5”, // 코드 리뷰 “temperature”: 0 } } } ```
Task 도구 활용
```typescript // 메인 에이전트에서 서브에이전트 호출 await task({ subagent_type: “code-refactor”, description: “Refactor authentication module”, prompt: “리팩토링할 파일: src/auth.ts\n목표: 스레드 안전성 개선” }) ```
3. 권한 프리셋
개발 모드 (빠른 반복)
```jsonc { “permission”: { “write”: “allow”, “edit”: “allow”, “bash”: “allow”, “doom_loop”: “allow” } } ```
프로덕션 모드 (안전 우선)
```jsonc { “permission”: { “write”: “ask”, “edit”: “ask”, “bash”: { “git ”: “allow”, “ls *”: “allow”, "": “ask” }, “doom_loop”: “deny” } } ```
읽기 전용 모드 (분석/리뷰)
```jsonc { “agent”: { “analyzer”: { “mode”: “primary”, “permission”: { “write”: “deny”, “edit”: “deny”, “bash”: { “git ”: “allow”, “find *”: “allow”, “grep *”: “allow”, "": “deny” } } } } } ```
4. MCP 서버 최적화
선별적 활성화
```jsonc { “mcp”: { // 항상 활성화 “git”: { “enabled”: true }, “github”: { “enabled”: true },
/ 프로젝트별 활성화 “playwright-extension”: { “enabled”: false }, / 웹 프로젝트만 “nixos”: { “enabled”: true }, // NixOS 환경
/ 작업별 활성화 “browserbase”: { “enabled”: false }, / 필요시만 “n8n-mcp”: { “enabled”: false } } } ```
agent-shell 통합
```elisp (defun agent-shell-enable-mcp-server (server-name) “특정 MCP 서버를 동적으로 활성화” (agent-shell-send-command (format “opencode mcp add %s” server-name)))
;; 예시: 브라우저 자동화 작업 시작 (agent-shell-enable-mcp-server “playwright-extension”) ```
🔄 워크플로우 예시
1. 코드 리팩토링 워크플로우
```
-
[Emacs] 파일 선택 및 컨텍스트 수집
- LSP: 타입 정보, 참조 위치
- Git: 변경 이력
-
[agent-shell] code-refactor 에이전트 호출 (agent-shell-send-with-context “이 모듈의 스레드 안전성을 개선해주세요” :lsp-context t :git-context t)
-
[OpenCode] code-refactor 에이전트 실행
- Model: deepseek/deepseek-coder
- Tools: read, edit, bash (git), lsp_find_references
- Permission: edit=allow (세션에서 always 선택 시)
-
[OpenCode] 리팩토링 수행
- LSP로 모든 참조 찾기
- 변경 사항 적용
- Git diff 확인
-
[agent-shell] code-reviewer 에이전트로 검증 (agent-shell-switch-agent “code-reviewer”) “방금 리팩토링한 코드를 리뷰해주세요”
-
[Emacs] 결과 확인 및 적용
```
2. PM 워크플로우
```
-
[agent-shell] PM 에이전트 활성화 (agent-shell-switch-agent “PM”)
-
[OpenCode] PM 에이전트 실행
- Model: openrouter/openai/gpt-5.1
- Tools: write=true (메모리 파일 생성)
- MCP: google-workspace-work (타임로그)
-
메모리 스캔 및 정리 ”~/claude-memory를 스캔하고 중복 문서를 통합해주세요”
-
Google Chat에 타임로그 “현재 작업 상태를 구글챗에 남겨주세요”
-
[Emacs] 정리된 메모리 확인 (dired ”~/claude-memory”)
```
3. 복잡한 버그 수정 워크플로우
```
-
[Emacs] 버그 리포트와 관련 파일 수집
-
[agent-shell] general 에이전트로 분석 “이 버그의 원인을 찾아주세요”
- 코드베이스 전체 검색
- 관련 파일 식별
-
[agent-shell] code-reviewer로 검증 (agent-shell-switch-agent “code-reviewer”) “방금 찾은 원인이 맞는지 확인해주세요”
-
[agent-shell] build 에이전트로 수정 (agent-shell-switch-agent “build”) “확인된 원인을 수정하고 테스트를 실행해주세요”
-
[OpenCode] 테스트 실행 및 검증
- bash: npm test
- 실패 시 재시도
-
[Emacs] Git 커밋 (magit-commit)
```
📁 설정 파일 구조
OpenCode 설정 (~/.config/opencode/ 또는 프로젝트/.opencode/)
``` .opencode/ ├── opencode.jsonc # 메인 설정 │ ├── model # 기본 모델 │ ├── agent # 커스텀 에이전트 │ ├── permission # 권한 설정 │ ├── mcp # MCP 서버 설정 │ └── command # 커스텀 명령어 │ ├── AGENTS.md # 에이전트 프롬프트 └── package.json # MCP 의존성 ```
프로젝트별 설정 예시
```jsonc // .opencode/opencode.jsonc { “instructions”: [“AGENTS.md”, “PROJECT_CONTEXT.md”], “model”: “anthropic/claude-sonnet-4-5”,
“agent”: { “build”: { “permission”: { “edit”: “ask”, “bash”: { “npm ”: “allow”, “git *”: “allow”, "": “ask” } } },
“test”: { “mode”: “subagent”, “model”: “deepseek/deepseek-coder”, “prompt”: “You are a test engineer. Write comprehensive tests.”, “tools”: { “write”: true, “edit”: true, “bash”: true }, “permission”: { “bash”: { “npm test*”: “allow”, “npm run test*”: “allow”, ”*”: “ask” } } } },
“command”: { “test”: { “template”: “Run tests and fix any failures”, “agent”: “test”, “model”: “deepseek/deepseek-coder” } },
“mcp”: { “git”: { “enabled”: true }, “github”: { “enabled”: true }, “playwright-extension”: { “enabled”: false } } } ```
🛠️ agent-shell 커스터마이징
Elisp 통합 함수
```elisp ;; ~/.emacs.d/init.el 또는 agent-shell 설정
(defun agent-shell-opencode-switch-agent (agent-name) “OpenCode 에이전트를 변경합니다.” (interactive (list (completing-read “Agent: ” ’(“build” “plan” “PM” “code-refactor” “code-reviewer” “security-forensics”)))) (agent-shell-send-command (format “/mode %s” agent-name)))
(defun agent-shell-opencode-switch-model (model-id) “OpenCode 모델을 변경합니다.” (interactive (list (completing-read “Model: ” ’(“anthropic/claude-sonnet-4-5” “anthropic/claude-haiku-4-5” “openrouter/openai/gpt-5.1-chat” “deepseek/deepseek-coder”)))) ;; ACP 프로토콜로 모델 변경 요청 (agent-shell-send-acp-request ‘setSessionModel `((modelId . ,model-id))))
(defun agent-shell-opencode-with-lsp-context () “현재 LSP 컨텍스트와 함께 프롬프트를 전송합니다.” (interactive) (let* ((symbol (thing-at-point ‘symbol)) (definition (lsp-find-definition)) (references (lsp-find-references)) (hover (lsp-hover)) (context (format “Symbol: %s\nDefinition: %s\nReferences: %d\nHover: %s” symbol definition (length references) hover)) (prompt (read-string “Prompt: ”))) (agent-shell-send-prompt (format “%s\n\n<lsp-context>\n%s\n</lsp-context>” prompt context))))
(defun agent-shell-opencode-enable-mcp (server-name) “MCP 서버를 동적으로 활성화합니다.” (interactive (list (completing-read “MCP Server: ” ’(“playwright-extension” “browserbase” “n8n-mcp” “supabase”)))) (agent-shell-send-command (format “opencode mcp add %s” server-name)))
;; 키바인딩 (with-eval-after-load ‘agent-shell (define-key agent-shell-mode-map (kbd “C-c a a”) ‘agent-shell-opencode-switch-agent) (define-key agent-shell-mode-map (kbd “C-c a m”) ‘agent-shell-opencode-switch-model) (define-key agent-shell-mode-map (kbd “C-c a l”) ‘agent-shell-opencode-with-lsp-context) (define-key agent-shell-mode-map (kbd “C-c a e”) ‘agent-shell-opencode-enable-mcp)) ```
ACP 프로토콜 직접 제어
```elisp (defun agent-shell-send-acp-request (method params) “ACP 프로토콜 요청을 직접 전송합니다.” (let ((request (json-encode `((jsonrpc . “2.0”) (id . ,(number-to-string (random 10000))) (method . ,method) (params . ,params))))) (agent-shell-send-raw request)))
;; 예시: 세션 모드 변경 (agent-shell-send-acp-request ‘setSessionMode ‘((sessionId . “current-session-id”) (modeId . “code-refactor”)))
;; 예시: 모델 변경 (agent-shell-send-acp-request ‘setSessionModel ‘((sessionId . “current-session-id”) (modelId . “deepseek/deepseek-coder”))) ```
🧪 테스트 시나리오
1. 권한 시스템 테스트
```
- 새 세션 시작
- 파일 쓰기 시도 → “once” 선택
- 다시 파일 쓰기 → 다시 물어봄 (once 효과)
- “always” 선택
- 서브에이전트로 전환 (code-refactor)
- 파일 쓰기 → 물어보지 않음 (세션 레벨 권한 공유)
```
2. 모델 전환 테스트
```
- 기본 모델 확인: Claude 3.7 Sonnet
- code-refactor 에이전트로 전환
- 자동으로 deepseek/deepseek-coder로 전환 확인
- 수동으로 모델 변경: anthropic/claude-haiku-4-5
- 작업 완료 후 기본 모델로 복귀
```
3. MCP 서버 동적 로딩 테스트
```
- 기본 MCP 서버만 활성화 (git, github)
- 브라우저 작업 필요 시:
- playwright-extension 활성화
- 작업 수행
- 비활성화 (메모리 절약)
- 대규모 스크래핑 필요 시:
- browserbase 활성화
- 병렬 작업 실행
```
📚 참고 자료
OpenCode 소스 코드
- 전체 코드: ~/repos/3rd/opencode/
- ACP 에이전트: packages/opencode/src/acp/agent.ts
- 권한 시스템: packages/opencode/src/permission/index.ts
- 에이전트 시스템: packages/opencode/src/agent/agent.ts
- 프로바이더: packages/opencode/src/provider/provider.ts
설정 파일
- OpenCode 설정: ~/sync/emacs/claude-config/opencode/opencode.jsonc
- 인증 정보: ~/.local/share/opencode/auth.json
- 현재 디바이스: ~/.current-device
관련 문서
- 브라우저 자동화 도구 비교
- OpenCode 공식 문서: https://opencode.ai/docs
- ACP 프로토콜: https://github.com/agentclientprotocol
🎬 다음 단계
단계 1: agent-shell 기본 통합
- OpenCode ACP 연결 안정화
- 에이전트 전환 UI 개선
- 권한 프리셋 설정
단계 2: LSP 통합
- MCP LSP 서버 개발
- Emacs LSP 컨텍스트 추출
- OpenCode 에이전트에 LSP 도구 추가
단계 3: 고급 기능
- 다중 모델 프로필 (작업별 자동 전환)
- MCP 서버 동적 로딩
- 세션 이력 관리 및 복원
단계 4: 워크플로우 최적화
- 코드 리뷰 자동화
- PM 에이전트 메모리 관리 통합
- Git 워크플로우 연동
💡 핵심 포인트
-
ACP 직접 연결의 장점
- Zed wrapper 없이 OpenCode 전체 기능 활용
- 다중 모델 선택 가능
- 서브에이전트 시스템 완전 지원
-
세션 레벨 권한 관리
- `always` 선택 시 서브에이전트 포함 전체 적용
- 세션별로 독립적인 권한 상태 유지
- 설정 파일로 기본 권한 프리셋 가능
-
서브에이전트의 힘
- 작업별로 최적화된 모델 사용
- 권한을 세밀하게 제어
- 도구 접근 제한 가능 (plan 모드: 읽기 전용)
-
LSP 통합 잠재력
- 코드 컨텍스트를 AI에게 정확하게 전달
- 타입 안전한 리팩토링
- 네비게이션 기반 코드 분석
-
MCP 생태계
- 필요한 서버만 선별적 활성화
- 메모리 및 성능 최적화
- 프로젝트별 커스터마이징
메타데이터
- 작성일: 2025-11-21 금요일 13:14
- 작성자: AI Assistant (OpenCode via ACP)
- 컨텍스트: agent-shell + OpenCode 커스터마이징
- 디바이스: LAPTOP
- 목적: 향후 커스터마이징 작업 시 빠른 문맥 전환
Comments