opendot: 되돌릴 수 있는 인터랙티브 터미널 AI 에이전트
요약
opendot은 사용자의 파일 및 셸 명령어에 직접 작동하는 인터랙티브 터미널 AI 에이전트입니다. 가장 큰 특징은 모든 행동을 스냅샷 처리하여 완벽하게 되돌릴 수 있다는 점입니다. 모델에 구애받지 않으며, LiteLLM과 Ollama를 통해 OpenAI, Anthropic 등 다양한 클라우드 및 로컬 환경에서 작동합니다.
핵심 포인트
- 모든 행동이 스냅샷으로 기록되어 완벽히 되돌릴 수 있음.
- 파일 편집과 셸 명령어 모두에 대해 되돌리기 기능 제공.
- 모델 독립적(model-agnostic)이며, Ollama를 통해 완전 로컬 실행 가능.
- 다양한 모델 및 프로바이더 지원 (OpenAI, Anthropic 등).

완전히 되돌릴 수 있는 인터랙티브 터미널 AI 에이전트입니다.
opendot은 사용자의 실제 파일과 셸에서 직접 작동합니다. 하지만 다른 터미널 에이전트와 달리, 그것이 취하는 모든 행동은 먼저 스냅샷 처리되므로, 무엇을 했는지 정확히 보고 깨끗하게 되돌릴 수 있습니다. 인-레포(in-repo) 편집뿐만 아니라 파일 및 셸 명령어에 대해 그렇습니다. 작업 공간 외부로 효과가 새어 나가는 명령어(네트워크, sudo, git push, 작업 디렉터리 외 삭제 등)는 실행 전에 플래그 지정되고 확인되며, 되돌릴 수 없는 것에 대한 솔직한 안내와 함께 진행됩니다.
이것이 opendot의 핵심입니다. 아무것도 놀라운 것이 없고 (거의) 아무것도 되돌릴 수 없지 않기 때문에 마음껏 풀어놓을 수 있는 에이전트라는 점입니다.
opendot은 모델에 구애받지 않습니다(model-agnostic). LiteLLM을 통해 모든 모델과 작동합니다 (OpenAI, Anthropic, Google, DeepSeek 등) 그리고 Ollama를 통해 완전히 로컬에서 실행됩니다. Ollama는 단순히 설치가 필요 없는 로컬 옵션일 뿐이며, 선호하는 백엔드를 사용하면 됩니다.
# 즉시 시도해 보세요, 설치 불필요
uvx opendot
# 권장 (격리된 전역 CLI)
...
opendot # 인터랙티브 채팅 열기
opendot -p "이 프로젝트 요약하기" # 스크립트/CI용 원샷(one-shot)
opendot --model claude-opus-4-5 # 특정 모델로 실행 (아래 참조)
...
채팅 내부에서는 슬래시 명령어(slash-commands)를 사용할 수 있습니다: /model (검색 가능한 모델 선택기), /provider (프로바이더 연결 + API 키 붙여넣기), /mcp, /composio, /log, /undo, /redo, /diff, /trace (모델 호출별 비용 + 타이밍), /clear, /compact, /help.
어떤 모델이든 작동합니다. 클라우드, 로컬 또는 Hugging Face입니다. 사용하려는 프로바이더에 대한 API 키가 필요합니다 (opendot은 BYO-key; 모델을 호스팅하지 않습니다). /model 및 /provider를 사용하여 채팅 내부에서 모델을 선택하고 키를 붙여넣거나, 환경 변수에 키를 설정하고 --model로 전달하세요:
프로바이더 이름은 키를 얻는 곳으로 연결됩니다.
Reasoning 모델들은 사고 과정을 실시간으로 스트리밍합니다.
로컬 OpenAI 호환 서버 (llama.cpp / llama-server, vLLM, LM Studio):
--api-base를 사용하여 해당 서버를 가리키고, openai/ 접두사가 붙은 모델을 사용합니다.
# 예: llama.cpp: llama-server -m model.gguf --port 8080
opendot --model openai/local --api-base http://localhost:8080/v1
실행되는 모델: 기본값은 gpt-5.1입니다. 만약 해당 키(OPENAI_API_KEY)가 설정되어 있지 않지만 다른 제공업체의 키가 설정되어 있다면, opendot은 실행 시 자동으로 그 제공업체로 전환됩니다. 예를 들어, DEEPSEEK_API_KEY만 설정된 경우, 아무 옵션 없이 opendot을 사용하면 deepseek/deepseek-chat을 사용합니다. 어떤 제공업체의 키도 발견되지 않으면, opendot은 정상적으로 시작하지만 첫 메시지에서 키를 설정하거나 /provider를 실행하라는 힌트를 보여줍니다 (순수한 제공업체 오류 대신). ollama/* 모델은 키가 필요하지 않으며, 로컬 Ollama만 있으면 됩니다.
재현성(reversibility)과 같은 아이디어이지만, 다른 축에 대한 것입니다. 재현성은 실행이 할 수 있는 손상을 제한하고, 예산(budget)은 지출을 제한합니다. 상한선을 설정하면 opendot은 누적 비용이나 토큰이 그 한계를 초과하는 순간 실행을 중단하며, 단계 제한까지 계속 실행하지 않습니다.
opendot -p "이 모듈을 리팩토링하세요" --usd 0.50 # 지출액 50센트에서 중지
pendot -p "..." --tokens 100000 # 토큰 10만 개에서 중지
모델에 알려진 가격이 있는 경우 --usd를 사용하고, 비율을 알 수 없는 자체 호스팅 api-base 모델의 경우 --tokens를 사용합니다 (로컬 모델은 비용이 $0이므로 토큰 제한이 의미가 있습니다). 기본값은 OPENDOT_MAX_USD / OPENDOT_MAX_TOKENS에서 가져옵니다.
만약 설정되지 않은(unset) 경우 유한하지 않다는 의미이며 기본값입니다. 용량 제한에 도달하면 명확하게 budget exceeded 메시지와 함께 실행이 종료됩니다.
/ token limit exceeded
기본적으로 opendot은 되돌릴 수 없거나 작업 공간을 벗어나는 모든 동작(그리고 일회성 실행은 질문할 대상이 없으므로 이를 거부합니다)에 대해 확인 절차를 거칩니다. CI 또는 비대면 실행의 경우, 권한 정책을 사용하여 무엇이 허용되는지 미리 결정할 수 있습니다:
opendot -p "테스트 스위트를 실행하고 실패를 수정" --yes # 프롬프트 자동 승인
opendot -p "..." --yes --deny "git push" --deny "rm -rf" # 하지만 이것들은 하드 블록
opendot -p "..." --allow "pytest" # 이것들만 승인```
`--yes`
원래는 프롬프트가 발생할 모든 것을 자동 승인합니다. 되돌릴 수 있는 속성은 변하지 않습니다. 모든 동작은 여전히 스냅샷 처리되어 취소할 수 있습니다.`--allow PATTERN`
/`--deny PATTERN`
단어 경계에서 동작(명령 또는 경로)과 일치합니다. 따라서 `--deny rm`은 `rm` 명령에만 일치하며, "refo**rm**at"에는 일치하지 않습니다. 또는 패턴에 `*`가 포함된 경우 glob으로 작동합니다.
/`?`
(예: `--deny "git push*"`,
`--deny "*.env"`). 둘 다 반복 가능합니다. 우선순위는 **거부(deny) > 허용(allow) > (**입니다. 따라서 광범위하게 자동 승인하면서도 특정 것은 절대 실행되지 않도록 보장할 수 있습니다.`--yes`
? approve : ask)
동일한 규칙을 프로젝트별로 `OPENDOT.md`(아래 참조)에 고정하여 저장소와 함께 이동시킬 수 있으며, CLI 플래그가 이를 병합합니다.
권한 정책은 보안 경계가 아닌 휴리스틱 게이트입니다(실행하는 스크립트는 여전히 작업 공간 외부로 도달할 수 있습니다). 이것이 중요한 비대면 실행의 경우, `--sandbox`는 보장을 **커널 수준**의 경계로 옮깁니다. 이는 턴을 컨테이너 내부에서 작업 공간 *복사본*을 대상으로 실행한 다음, 성공 시 결과적인 diff만 커밋합니다.
opendot -p "리팩토링하고 테스트를 실행" --sandbox # 컨테이너 내 격리 실행
opendot -p "..." --sandbox --sandbox-net # 네트워크 허용 (기본적으로 비활성화)
- **docker 또는 podman**이 필요합니다. 둘 다 설치되어 있지 않으면, `--sandbox`
오류 — 직접 실행으로 조용히 폴백하지 않습니다. - 컨테이너 내부에서는 기본적으로 네트워크가 **비활성화**되어 있으며, 모델이 필요한 API 키만 포워딩되고 환경의 다른 것은 아무것도 전달되지 않습니다. - 성공하면 작업 공간의 차이(diff)가 가역성 엔진을 통해 커밋됩니다 (스냅샷 처리 및 취소 가능하며, 매우 큰 파일의 경우 스냅샷 크기 제한에 따름); 컨테이너 종료 코드가 0이 아니면 아무것도 적용되지 않습니다.
- 일회성 실행만 가능 (`-p`)
) — 헤드리스 컨테이너 내부에서 상호작용 세션을 하는 것은 유용하지 않습니다. 일반적인 직접 터미널 실행은 이를 전달하지 않을 때 변경되지 않습니다.
컨테이너 이미지(`--sandbox-image`, 기본값 `python:3.12-slim`)에는 opendot이 사용 가능해야 합니다. 이것이 opendot이 제공하는 가장 강력한 경계이며, 분류기(classifier)는 직접 실행을 위한 설명자(explainer)로 유지됩니다.
opendot은 MCP 클라이언트입니다: 모든 MCP 서버를 연결할 수 있으며, 해당 도구들은 내장된 도구들과 함께 에이전트에게 사용 가능해집니다. **`/mcp`** (서버 및 상태의 드롭다운 목록에
opendot는 외부 도구가 무엇을 하는지 알 수 없기 때문에, **모든 MCP 도구 호출은 되돌릴 수 없는 것(irreversible)으로 처리됩니다** — 실행 전에 확인되고 원장(ledger)에 ✗로 표시됩니다. 사용자가 내장한 파일/셸 액션은 평소처럼 스냅샷되고 취소 가능합니다.
MCP 외에도, opendot는 **사용자의 자체** Composio API 키를 사용하여 Composio의 1000개 이상의 앱 도구(Gmail, Slack, GitHub, Notion, Linear 등)에 연결할 수 있습니다. 채팅에서 `/composio`만 사용하세요:
- 첫 번째 `/composio`는 Composio API 키를 요청합니다 (사용자 홈 디렉터리의 `~/.opendot/composio.json`에 저장되며, 소유자만 읽을 수 있음). - 그 후에는 `/composio`가 사용 가능한 앱 목록을 보여줍니다. 하나를 선택하세요 — OAuth가 필요한 경우, opendot는 브라우저를 열어 인증하도록 하고 완료될 때까지 기다립니다; 직접 연결/API 키 커넥터는 즉시 활성화됩니다. - 활성화된 앱은 사이드바에 나타나며, 해당 도구들은 다음 실행 시 로드됩니다.
Composio 도구는 외부 서비스에 접근하므로 — MCP와 마찬가지로 **모든 호출이 되돌릴 수 없는 것으로 처리됩니다**: 먼저 확인되고 원장에 ✗로 표시됩니다.
프로젝트에 `OPENDOT.md`를 추가하세요. 이 문서의 내용은 에이전트에게 컨텍스트(context)로 제공됩니다. 또한 `opendot` 블록을 사용하여 무엇을 스냅샷할지 제어할 수도 있습니다:
snapshot: dist
# never snapshot these:
skip: data, *.log
# permission policy (same as --allow/--deny; comma-separated, may contain spaces):
allow: pytest, ruff
deny: git push, rm -rf
기본적으로 opendot는 스냅샷을 찍을 때 `.git`, `node_modules`, 가상 환경(virtualenvs), 빌드 캐시를 건너뜁니다 — 사용자의 규칙이 어느 방향으로든 이를 재정의합니다. `allow:` / `deny:` 목록은 프로젝트의 권한 정책을 설정하며; `--allow` / `--deny` CLI 플래그는 여기에 병합됩니다.
- 모든 파일 쓰기 또는 셸 명령어 실행 전에, opendot는 작업 디렉터리를 **콘텐츠 주소 지정 저장소(content-addressed store)**인 `~/.opendot`에 스냅샷합니다 (각 고유한 파일은 한 번만 저장되므로 스냅샷 비용이 저렴함). - 모든 액션은 **추가 전용 원장(append-only ledger)**에 기록되며, 이는 `opendot log`로 검사할 수 있고, `▸ you are here`를 포함하는 타임라인을 보여줍니다.
cursor는 한눈에 어떤 액션이 적용되었고 어떤 것이 되돌려졌는지(그리고 여전히 다시 실행 가능한지) 볼 수 있게 해줍니다. `opendot undo`
작업 공간을 선택한 지점으로 정확하게 복원하고, `opendot redo`
방금 취소했던 것을 다시 적용합니다 (잘못된 취소조차 되돌릴 수 있습니다). `opendot diff`
사전 실행(dry run)입니다. 취소 또는 복원 시 어떤 파일이 변경될지 커밋하기 전에 정확하게 보여줍니다. - 세션은 프로젝트별로 유지되므로, `opendot resume`
이전에 중단했던 대화와 그 기록을 이어서 진행합니다. - 보수적인 **분류기(classifier)**가 어떤 셸 명령어가 작업 공간 내에 포함되는지(자동 실행, 취소 가능) 아니면 외부로 나가는지(먼저 확인 후, 되돌릴 수 없는 것으로 표시) 결정합니다. 이는 체인의 각 명령어를 독립적으로 분류하며 (따라서 안전한 `a && b`
이 위험한 `b`를 프롬프트 너머로 몰래 가져갈 수 없게 하고), 불투명한 인터프리터(`python`, `bash`, `docker`, …)는 스크립트가 무엇이든 할 수 있기 때문에 먼저 확인(confirm-first) 대상으로 취급합니다. 모호할 때는 사용자에게 묻습니다. 또한, 내장 파일 쓰기는 OS 수준에서 추가적으로 포함되므로 심볼릭 링크 경로를 통해 작업 공간 외부로 리디렉션될 수 없습니다.
정직한 경계: opendot은 사용자의 기기 외부로 나가는 효과(보낸 이메일, 유실된 원격 데이터베이스, `git push` 등)는 되돌릴 수 없습니다. 그러므로 실행하기 *전에* 알려주며, 그렇지 않은 척하지 않습니다.
이 분류기는 **언제 물어볼지 결정하는 휴리스틱이며 보안 경계가 아닙니다.** 이는 셸 텍스트를 읽기 때문에 불투명한 서브프로세스가 무엇을 하는지 완전히 설명할 수는 없습니다 ( `python foo.py`로 실행되는 스크립트는 `python -c`만큼 알 수 없으며), 그리고 의도적인 적대자는 심볼릭 링크 교체를 할 수 있습니다. 이것이 인터프리터가 먼저 확인 대상으로 지정되고 내장 파일 쓰기가 열릴 때 포함되는 이유입니다. 하지만 정직한 설명은 분류기가 왜 확인 절차가 신중한지 *설명*하는 것이지, 악의적인 입력에 대한 보장은 아니라는 것입니다. 실제 경계가 필요할 때는 `--sandbox`
작업 공간 복사본 내에서 턴을 컨테이너 내부에서 실행하고, 결과로 나온 diff만 커밋합니다. 이는 휴리스틱 방식이 아닌 커널 기반의 격리(kernel-enforced containment)를 사용합니다 (위의 Sandbox 모드 섹션 참조). 컨테이너 런타임이 없는 Linux 호스트용 오버레이fs 계층은 후속 작업으로 남아 있습니다 (#130).
**스냅샷을 의도적으로 건너뛰기.** opendot이 셸 명령을 실행할 때 먼저 스냅샷을 찍습니다. 하지만 삭제되기를 *원하는* 것(비밀 정보를 안전하게 지우는 경우)이나 크기가 매우 큰 임시 파일의 경우, 그 스냅샷은 복구 가능한 사본을 저장소에 보관합니다.
opendot이 실행하는 명령어 앞에 `OPENDOT_NO_SNAPSHOT=1`을 붙이면 해당 명령어에 대한 스냅샷을 건너뜁니다:
OPENDOT_NO_SNAPSHOT=1 shred secrets.txt
이 동작은 여전히 감사 추적(audit trail)을 위해 기록되지만, 되돌릴 수 없다고 표시됩니다 (스냅샷 백업 없음). 이는 opendot 자체가 실행하는 명령어에만 영향을 미칩니다. 사용자가 opendot 외부에서 자신의 셸에서 실행하는 것은 어떤 경우에도 스냅샷이 찍히거나 기록되지 않습니다. 스냅샷 대상 경로를 영구적으로 제외하려면 `OPENDOT.md`의 `skip:` 규칙을 사용하십시오.
**도구 출력 제한.** 도구 결과가 너무 커서 컨텍스트 창을 압도하는 것을 막기 위해 잘립니다. `OPENDOT_MAX_TOOL_OUTPUT`을 설정하여 도구별 문자 제한(기본값 `30000`)을 변경할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub AI Tools의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기