관련노트

히스토리

  • [2025-11-21 Fri 13:20] 에이전트에게 부탁

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 APIACP → 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 정보를 활용하여 더 정확한 코드 수정
구현 방향
  1. MCP 서버로 LSP 래퍼 개발 ```typescript // mcp-lsp-server { “name”: “lsp”, “tools”: [ “lsp_find_definition”, “lsp_find_references”, “lsp_get_hover”, “lsp_get_completion” ] } ```

  2. 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)))) ```

  3. 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. 코드 리팩토링 워크플로우

```

  1. [Emacs] 파일 선택 및 컨텍스트 수집

    • LSP: 타입 정보, 참조 위치
    • Git: 변경 이력
  2. [agent-shell] code-refactor 에이전트 호출 (agent-shell-send-with-context “이 모듈의 스레드 안전성을 개선해주세요” :lsp-context t :git-context t)

  3. [OpenCode] code-refactor 에이전트 실행

    • Model: deepseek/deepseek-coder
    • Tools: read, edit, bash (git), lsp_find_references
    • Permission: edit=allow (세션에서 always 선택 시)
  4. [OpenCode] 리팩토링 수행

    • LSP로 모든 참조 찾기
    • 변경 사항 적용
    • Git diff 확인
  5. [agent-shell] code-reviewer 에이전트로 검증 (agent-shell-switch-agent “code-reviewer”) “방금 리팩토링한 코드를 리뷰해주세요”

  6. [Emacs] 결과 확인 및 적용

```

2. PM 워크플로우

```

  1. [agent-shell] PM 에이전트 활성화 (agent-shell-switch-agent “PM”)

  2. [OpenCode] PM 에이전트 실행

    • Model: openrouter/openai/gpt-5.1
    • Tools: write=true (메모리 파일 생성)
    • MCP: google-workspace-work (타임로그)
  3. 메모리 스캔 및 정리 ”~/claude-memory를 스캔하고 중복 문서를 통합해주세요”

  4. Google Chat에 타임로그 “현재 작업 상태를 구글챗에 남겨주세요”

  5. [Emacs] 정리된 메모리 확인 (dired ”~/claude-memory”)

```

3. 복잡한 버그 수정 워크플로우

```

  1. [Emacs] 버그 리포트와 관련 파일 수집

  2. [agent-shell] general 에이전트로 분석 “이 버그의 원인을 찾아주세요”

    • 코드베이스 전체 검색
    • 관련 파일 식별
  3. [agent-shell] code-reviewer로 검증 (agent-shell-switch-agent “code-reviewer”) “방금 찾은 원인이 맞는지 확인해주세요”

  4. [agent-shell] build 에이전트로 수정 (agent-shell-switch-agent “build”) “확인된 원인을 수정하고 테스트를 실행해주세요”

  5. [OpenCode] 테스트 실행 및 검증

    • bash: npm test
    • 실패 시 재시도
  6. [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. 권한 시스템 테스트

```

  1. 새 세션 시작
  2. 파일 쓰기 시도 → “once” 선택
  3. 다시 파일 쓰기 → 다시 물어봄 (once 효과)
  4. “always” 선택
  5. 서브에이전트로 전환 (code-refactor)
  6. 파일 쓰기 → 물어보지 않음 (세션 레벨 권한 공유)

```

2. 모델 전환 테스트

```

  1. 기본 모델 확인: Claude 3.7 Sonnet
  2. code-refactor 에이전트로 전환
  3. 자동으로 deepseek/deepseek-coder로 전환 확인
  4. 수동으로 모델 변경: anthropic/claude-haiku-4-5
  5. 작업 완료 후 기본 모델로 복귀

```

3. MCP 서버 동적 로딩 테스트

```

  1. 기본 MCP 서버만 활성화 (git, github)
  2. 브라우저 작업 필요 시:
    • playwright-extension 활성화
    • 작업 수행
    • 비활성화 (메모리 절약)
  3. 대규모 스크래핑 필요 시:
    • 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

관련 문서

🎬 다음 단계

단계 1: agent-shell 기본 통합

  • OpenCode ACP 연결 안정화
  • 에이전트 전환 UI 개선
  • 권한 프리셋 설정

단계 2: LSP 통합

  • MCP LSP 서버 개발
  • Emacs LSP 컨텍스트 추출
  • OpenCode 에이전트에 LSP 도구 추가

단계 3: 고급 기능

  • 다중 모델 프로필 (작업별 자동 전환)
  • MCP 서버 동적 로딩
  • 세션 이력 관리 및 복원

단계 4: 워크플로우 최적화

  • 코드 리뷰 자동화
  • PM 에이전트 메모리 관리 통합
  • Git 워크플로우 연동

💡 핵심 포인트

  1. ACP 직접 연결의 장점

    • Zed wrapper 없이 OpenCode 전체 기능 활용
    • 다중 모델 선택 가능
    • 서브에이전트 시스템 완전 지원
  2. 세션 레벨 권한 관리

    • `always` 선택 시 서브에이전트 포함 전체 적용
    • 세션별로 독립적인 권한 상태 유지
    • 설정 파일로 기본 권한 프리셋 가능
  3. 서브에이전트의 힘

    • 작업별로 최적화된 모델 사용
    • 권한을 세밀하게 제어
    • 도구 접근 제한 가능 (plan 모드: 읽기 전용)
  4. LSP 통합 잠재력

    • 코드 컨텍스트를 AI에게 정확하게 전달
    • 타입 안전한 리팩토링
    • 네비게이션 기반 코드 분석
  5. MCP 생태계

    • 필요한 서버만 선별적 활성화
    • 메모리 및 성능 최적화
    • 프로젝트별 커스터마이징

메타데이터

  • 작성일: 2025-11-21 금요일 13:14
  • 작성자: AI Assistant (OpenCode via ACP)
  • 컨텍스트: agent-shell + OpenCode 커스터마이징
  • 디바이스: LAPTOP
  • 목적: 향후 커스터마이징 작업 시 빠른 문맥 전환