DMF_Crawler/docs/research/05a-agy-cli-ssot.md
Yun Chan 56a6e2da93 chore: 저장소 구조 정리 및 문서화, 첫 커밋
- src/dist 산출물 분리 원칙 정리(.gitignore, .gitattributes)
- 루트 및 주요 폴더(config/scripts/prompts/tests/src, 런타임 폴더 5종)에
  안내용 README.md 추가
- CHANGELOG.md, LICENSE, docs/ops/05-release-and-versioning.md 추가
- docs/README.md 문서 지도 갱신
2026-09-04 09:25:44 +09:00

1079 lines
56 KiB
Markdown

# Google Antigravity CLI (`agy`) 정본 — 설치 · 인증 · Headless 실행
> **이 문서의 역할**: 이 프로젝트가 채택한 유일한 AI 에이전트 CLI 인 `agy` 에 대한 단일 정본(SSOT). 설치·인증·비대화형 실행·설정·권한·함정을 여기 한 곳에 모은다. 다른 문서는 이 문서를 참조하고 중복 서술하지 않는다.
**작성 기준일**: 2026-09-02
**검증 방식**: 공식 문서 직접 열람(WebFetch) + 로컬 머신 실측(Windows 11 Pro 10.0.26220, `agy` v1.1.22)
**실측 머신 경로**: `C:\Users\encep\AppData\Local\agy\bin\agy.exe`
---
## 0. 한눈에 보기
- `agy` 는 Google Antigravity 의 터미널 에이전트 CLI 다. **Go 로 작성된 단일 실행 파일**이며 Windows 에서 `%LOCALAPPDATA%\agy\bin\agy.exe` 에 설치된다. 실측 바이너리 크기 약 **186 MB**.
- **Headless 는 1급 기능이다.** `agy -p "<프롬프트>" --output-format json` 으로 완전한 비대화형 실행이 되고, `status` / `response` / `usage` / `error` 를 담은 JSON 봉투를 stdout 으로 뱉는다. 종료 코드도 규약이 있다.
- **인증 토큰은 파일에 저장된다.** 실측 결과 `~/.gemini/antigravity-cli/antigravity-oauth-token` 평문 JSON. 공식 문서는 OS 키링(Windows Credential Manager)을 말하지만, 실측 머신의 `cmdkey /list` 에는 관련 항목이 없었다. **이는 작업 스케줄러 무인 실행에 결정적으로 유리하다** (키링 잠금 문제를 피함). 대신 파일 유출 = 계정 탈취이므로 보안 취급 주의.
- **최초 1회는 대화형 로그인이 필수다.** 헤드리스는 캐시된 자격증명을 쓴다. 따라서 부트스트랩 설계에 "미인증 감지 → 사용자에게 프롬프트 창 띄우기" 경로가 반드시 필요하다.
- **`--json-schema` 는 신뢰할 수 없다(실측).** 문서상 `structured_output` 필드로 검증된 객체가 온다고 하지만, 실측에서는 `structured_output` 이 없고 `response` 안에 JSON 조각이 4번 반복되어 섞여 나왔다. **파이프라인은 반드시 자체 파싱·검증 계층을 둬야 한다.**
- **첫 호출 오버헤드가 크다(실측).** "OK 한 단어만 답하라"는 프롬프트에 **input_tokens 28,317 / 33.7초**. 시스템 프롬프트와 워크스페이스 인덱싱 비용이다. 짧은 작업을 여러 번 호출하는 설계는 비싸다. **호출을 묶어라.**
- 기본 `--print-timeout`**5분**. 배치에서는 명시적으로 늘려 잡는다.
- 자동 백그라운드 self-update 가 돌기 때문에 배치 실행 중 업데이트 충돌 위험이 있다. `AGY_CLI_DISABLE_AUTO_UPDATE=true` 로 끄고 주 1회 계획 업데이트하는 것을 권한다.
---
## 1. 목차
1. [제품 정체성과 위치](#2-제품-정체성과-위치)
2. [설치](#3-설치)
3. [인증](#4-인증)
4. [Headless(비대화형) 실행 정본](#5-headless비대화형-실행-정본)
5. [출력 포맷 3종 상세](#6-출력-포맷-3종-상세)
6. [구조화 출력과 그 함정](#7-구조화-출력과-그-함정)
7. [모델 · 에이전트 · 추론 강도](#8-모델--에이전트--추론-강도)
8. [권한 모델](#9-권한-모델)
9. [실행 모드](#10-실행-모드)
10. [설정 파일 전체 키](#11-설정-파일-전체-키)
11. [디렉터리·파일 레이아웃 실측](#12-디렉터리파일-레이아웃-실측)
12. [종료 코드와 오류 처리](#13-종료-코드와-오류-처리)
13. [크레딧·쿼터](#14-크레딧쿼터)
14. [트러블슈팅](#15-트러블슈팅)
15. [배치·스케줄러 관점 체크리스트](#16-배치스케줄러-관점-체크리스트)
16. [주변 생태계](#17-주변-생태계)
17. [부록 A. 출처 목록](#부록-a-출처-목록)
18. [부록 B. 미해결 질문 / 실측 필요 항목](#부록-b-미해결-질문--실측-필요-항목)
---
## 2. 제품 정체성과 위치
공식 문서 원문 인용:
> "The Antigravity CLI is the lightweight Terminal User Interface (TUI) surface of Antigravity. It brings the same core agentic capabilities as Antigravity 2.0 (such as multi-step reasoning, multi-file editing, tool calling, and conversation history) directly to your terminal."
| 항목 | 내용 |
|---|---|
| 명령어 이름 | `agy` (Windows: `agy.exe`) |
| 문서 기준 버전 | v1.1.22 |
| 실측 설치 버전 | **1.1.22** (`agy --version`) |
| winget 최신 | 1.1.23 (`Google.AntigravityCLI`) |
| 구현 언어 | Go |
| 발표 | 2026-05-19 Google I/O |
| 위치 | Antigravity 2.0(데스크톱/IDE)과 **동일한 에이전트 코어**, 설정 동기화, 대화 상호 export |
| Gemini CLI 와의 관계 | **후속 제품**. `/docs/cli/gcli-migration` 에 확장·스킬·설정 일괄 마이그레이션 가이드 존재 |
| 형제 제품 | Antigravity IDE (`agy-ide`, winget `Google.AntigravityIDE` v2.5.5), Antigravity 데스크톱 (`Google.Antigravity` v2.11.0) |
**혼동 주의**: `agy` (CLI) 와 `agy-ide` (IDE 런처) 는 다른 명령이다. 2026년 5월에 IDE 런처가 `agy-ide` 로 이름이 바뀌었다.
주요 기능(공식 개요 페이지):
- 키보드 중심 인터페이스
- 다단계 추론, 다중 파일 편집
- 도구 호출, 대화 이력
- **헤드리스 모드 및 백그라운드 작업 실행**
- 샌드박스 환경, 서브에이전트
- MCP, 플러그인, 스킬 확장
---
## 3. 설치
### 3.1 공식 설치 명령 (OS별)
```bash
# macOS / Linux
curl -fsSL https://antigravity.google/cli/install.sh | bash
```
```powershell
# Windows PowerShell
irm https://antigravity.google/cli/install.ps1 | iex
```
```cmd
:: Windows CMD
curl -fsSL https://antigravity.google/cli/install.cmd -o install.cmd && install.cmd && del install.cmd
```
### 3.2 설치 위치
| OS | 경로 |
|---|---|
| macOS / Linux | `~/.local/bin/agy` |
| Windows | `C:\Users\<username>\AppData\Local\agy\bin\agy.exe` |
실측 확인:
```
$ where.exe agy
C:\Users\encep\AppData\Local\agy\bin\agy.exe
C:\Users\encep\AppData\Local\Microsoft\WinGet\Links\agy.EXE
```
> **함정**: `where` 에 두 경로가 잡힌다. winget 으로도 설치된 이력이 있으면 WinGet Links 심볼릭이 함께 잡힌다. **배치 스크립트는 절대 경로를 고정해서 쓰는 것이 안전하다.**
### 3.3 `install.ps1` 동작 해부 (전문 확인 완료)
설치 스크립트를 직접 내려받아 전문을 읽었다. 동작 순서:
1. **TLS 1.2 강제** (ConstrainedLanguage 모드가 아닐 때만)
2. **인자 파싱**: `-d` / `--dir` 로 설치 디렉터리 커스터마이즈. 나머지 인자는 뒤의 `agy install` 로 패스스루
3. **기존 설치 감지**: `agy.exe` 가 이미 있으면 **아무 것도 하지 않고 종료 코드 0** 으로 빠진다. 메시지:
> `Notice: 'agy.exe' is already installed at <path>.`
> `The Antigravity CLI automatically self-updates in the background.`
> `If you want to perform a fresh installation, delete the binary first:`
> ` Remove-Item "<path>" -Force`
4. **아키텍처 감지**: `PROCESSOR_ARCHITEW6432` 우선, 없으면 `PROCESSOR_ARCHITECTURE`. `AMD64``windows_amd64`, `ARM64``windows_arm64`. 그 외는 치명적 오류.
5. **매니페스트 다운로드**:
`https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/<platform>.json`
`version`, `url`, `sha512` 획득
6. **스테이징 다운로드**: `%LOCALAPPDATA%\antigravity\staging\agy.exe`
7. **SHA512 검증**: `Get-FileHash` 우선, 실패 시 `certutil -hashfile ... SHA512` 폴백. 불일치 시:
> `Security Halt: Checksum verification failed. The downloaded file may be corrupted or compromised.`
8. **배치 및 Unblock**: `Copy-Item``Unblock-File` (Mark-of-the-Web 제거)
9. **네이티브 셋업 핸드오프**: `& $binaryPath install $setupFlags` 실행. 실패해도 흡수(Unix 의 `|| true` 와 동일).
10. **finally 블록에서 스테이징 파일 정리**
**배치 관점 시사점**:
- 이미 설치돼 있으면 재실행이 **안전(idempotent)** 하다. 부트스트랩 스크립트에서 무조건 호출해도 된다.
- **업데이트 목적으로는 쓸 수 없다.** 기존 바이너리가 있으면 그냥 빠진다. 업데이트는 자동 self-update 또는 `agy update` 담당.
- 무결성 검증이 내장돼 있어 별도 검증 불필요.
- `-d/--dir` 로 설치 경로를 프로젝트 전용으로 고정할 수 있다.
### 3.4 `agy install` 서브커맨드 (실측)
```
$ agy install --help
Usage: agy.exe install [flags]
Configure environment paths and shell settings
Flags:
--dir Custom directory target to configure PATH for
-h Show help
--help Show help
--skip-aliases Bypasses shell profile alias purging
--skip-path Bypasses shell profile PATH appending
```
`agy install`**바이너리 설치가 아니라 환경 구성**(PATH 추가, 셸 프로필 별칭 정리)이다. install.ps1 의 9단계가 이걸 호출한다.
### 3.5 winget 경로
```
$ winget list --name antigravity
이름 장치 ID 버전 사용 가능 원본
--------------------------------------------------------------------
Antigravity 2.11.0 Google.Antigravity 2.11.0 winget
Antigravity CLI Google.AntigravityCLI 1.1.10 1.1.23 winget
Antigravity IDE (User) Google.AntigravityIDE 2.5.5 winget
```
무인 설치 명령(권장 형태):
```powershell
winget install --id Google.AntigravityCLI --silent --accept-package-agreements --accept-source-agreements
```
> ⚠️ **미검증 / 조사 필요**: winget 은 SYSTEM 계정·서비스 세션에서 동작하지 않는 것으로 알려져 있다. 작업 스케줄러가 사용자 계정으로 실행될 때는 문제없지만, SYSTEM 으로 실행하면 실패할 수 있다. → `docs/research/09-agy-bootstrap-and-provisioning.md` 에서 결론.
### 3.6 자동 업데이트
- 공식 문서: "The Antigravity CLI automatically self-updates in the background."
- 비활성화 환경변수: **`AGY_CLI_DISABLE_AUTO_UPDATE=true`**
- 업데이터 락 파일: `~/.gemini/antigravity-cli/updater/update.lock`
- 락 충돌 경고 문자열: `Warning: another background updater process is already active (update.lock)`
- 수동 업데이트 서브커맨드: `agy update` (실측: `agy update --help``Usage of update:` 만 출력하고 플래그 목록이 비어 있음 — 인자 없이 쓰는 명령)
**배치 정책 권고**: 06:00 배치 실행 시 `AGY_CLI_DISABLE_AUTO_UPDATE=true` 를 설정해 업데이트가 끼어들지 않게 하고, 별도 주간 작업에서 `agy update` 를 돌린다. 배치 도중 바이너리가 교체되면 실행 실패 또는 예측 불가 동작이 발생한다.
---
## 4. 인증
### 4.1 공식 문서상 인증 경로
| 방식 | 동작 | 비대화형 적합성 |
|---|---|---|
| **로컬 키링 로그인** | OS 네이티브 보안 키링(Apple Keychain, Linux Secret Service/dbus, **Windows Credential Manager**) 접근. 유효한 자격증명이 있으면 조용히 인증, 없으면 기본 브라우저로 OAuth 로그인 | 최초 1회 대화형 필요 |
| **원격 SSH OAuth** | SSH 감지 시 "manual URL loop" — 인증 URL 을 로컬 브라우저에 붙여넣고, 받은 영숫자 코드를 터미널에 다시 붙여넣기 | 대화형 |
| **Gemini API 키** | `~/.gemini/antigravity-cli/settings.json``modelProvider: "gemini"` 설정 + `GEMINI_API_KEY` 환경변수 export. 문서 원문: "The CLI skips the sign-in screen and opens the main interface directly." | **완전 비대화형 가능** |
| **커스텀 엔드포인트** | `GOOGLE_GEMINI_BASE_URL` 환경변수로 Gemini 호환 대체 엔드포인트 지정 | 부가 |
| 로그아웃 | CLI 안에서 `/logout` — 계정 연결 해제 및 OS 키링의 저장된 인증 프로필 삭제 | — |
### 4.2 실측: 토큰은 파일에 있다
```
$ ls ~/.gemini/antigravity-cli/
-rw-r--r-- antigravity-oauth-token # 504 바이트
```
파일 내용 형태 (값은 마스킹):
```json
{"token":{"access_token":"ya29.<REDACTED>","token_type":"...", ...}}
```
한편 Windows 자격증명 관리자에는 관련 항목이 없었다:
```
$ cmdkey /list | grep -i -E "antigrav|agy|google"
(결과 없음)
```
**결론과 시사점**:
| 사실 | 배치 운영에 대한 함의 |
|---|---|
| 토큰이 사용자 프로필 아래 **평문 파일**로 존재 | 작업 스케줄러가 **동일 사용자 계정**으로 실행되면 키링 잠금 문제 없이 인증이 통과한다. 이것이 이 프로젝트의 인증 전략의 근거다. |
| Windows Credential Manager 를 쓰지 않음(실측) | S4U(암호 저장 안 함) 방식으로 실행해도 키링 접근 실패 위험이 낮다. 다만 **사용자 프로필이 로드돼야** 파일에 접근 가능 — SYSTEM 계정 실행은 금지. |
| 토큰 = 계정 접근 권한 | 이 파일을 백업·전송·로그에 남기지 않는다. 리포지토리에 절대 커밋 금지. `.gitignore` 에 명시. |
| access_token 은 만료된다 | refresh 실패 시 배치가 인증 오류로 죽는다. **미인증 감지 → 사용자 알림 경로가 반드시 필요하다.** |
### 4.3 인증 상태 비대화형 확인 방법
권장 헬스체크(가장 저렴한 형태):
```powershell
$env:AGY_CLI_DISABLE_AUTO_UPDATE = "true"
$out = & "$env:LOCALAPPDATA\agy\bin\agy.exe" -p "Reply with exactly: PONG" --output-format json --print-timeout 90s 2>&1
$obj = $out | ConvertFrom-Json
if ($obj.status -ne "SUCCESS") { Write-Error $obj.error }
```
실측 결과(정상 인증 상태):
```json
{"conversation_id":"eab39b4e-0a09-494f-9ded-87aaf2b9c6e1","status":"SUCCESS","response":"OK\n","duration_seconds":33.6771974,"num_turns":1,"usage":{"input_tokens":28317,"output_tokens":44,"thinking_tokens":43,"cache_read_tokens":0,"total_tokens":28361}}
```
종료 코드 `0`.
> ⚠️ **비용 주의**: 이 헬스체크 한 번에 input 28k 토큰이 든다. 매 배치마다 별도 헬스체크를 돌리지 말고, **실제 작업 호출의 결과로 인증 실패를 판정**하라.
---
## 5. Headless(비대화형) 실행 정본
### 5.1 전체 플래그 표 (실측 `agy --help` 원문 기준)
| 플래그 | 기본값 | 설명 |
|---|---|---|
| `-p`, `--print`, `--prompt` | — | 단일 프롬프트를 비대화형으로 실행하고 응답을 출력 |
| `-i`, `--prompt-interactive` | — | 초기 프롬프트를 실행하고 대화형 세션을 계속 |
| `--output-format` | `text` | 출력 포맷: `text` \| `json` \| `stream-json` |
| `--input-format` | `text` | 입력 포맷: `text` \| `stream-json`. `stream-json` 은 stdin 에서 NDJSON 을 한 줄에 하나씩 읽어 턴마다 실행하며, **`--output-format stream-json` 을 요구** |
| `--json-schema` | — | 구조화 출력 강제용 JSON 스키마 문자열 또는 스키마 파일 경로. stream-json 에서는 최종 result 에만 적용 |
| `--model` | — | 이 세션의 모델 (`agy models` 참조) |
| `--effort` | — | 추론 강도: `low` \| `medium` \| `high` |
| `--agent` | — | 이 세션의 에이전트 (`agy agents` 참조) |
| `-c`, `--continue` | `false` | 가장 최근 대화 이어가기 |
| `--conversation` | — | 대화 ID 로 이전 대화 재개 |
| `--dangerously-skip-permissions` | `false` | 모든 도구 권한 요청을 자동 승인 |
| `--disable-slash-commands` | — | print 모드에서 슬래시 명령·스킬 확장 비활성화 |
| `--print-timeout` | `5m0s` | print 모드 응답 대기 제한 시간 |
| `--mode` | — | 에이전트 실행 모드 (`accept-edits`, `plan`) |
| `--sandbox` | `false` | 터미널 제한이 걸린 샌드박스로 실행 |
| `--add-dir` | `[]` | 워크스페이스에 디렉터리 추가 (반복 가능) |
| `--project` | — | 이 세션의 프로젝트 ID 또는 이름 |
| `--new-project` | — | 이 세션용 새 프로젝트 생성 |
| `--log-file` | — | CLI 로그 파일 경로 재정의 |
### 5.2 서브커맨드 (실측)
| 서브커맨드 | 설명 |
|---|---|
| `agent` / `agents` | 사용 가능한 에이전트 목록 |
| `changelog` | 변경 로그·릴리스 노트 |
| `help` | 서브커맨드 도움말 |
| `install` | 환경 경로·셸 설정 구성 |
| `mcp` | MCP 서버 관리 (add, remove, list, enable, disable) |
| `mic-serve` | 이 머신의 마이크를 다른 호스트의 CLI 에 제공 |
| `models` | 사용 가능한 모델 목록 |
| `plugin` / `plugins` | 플러그인 관리 (install, uninstall, list, enable, disable) |
| `update` | CLI 업데이트 |
### 5.3 표준 스트림 규약
공식 문서 원문:
> Output goes to `stdout`; diagnostics (errors, auth prompts, progress) go to `stderr`.
**배치 설계**: `stdout` 만 파싱하고, `stderr` 는 통째로 로그 파일에 남긴다. 둘을 섞으면(`2>&1`) JSON 파싱이 깨진다.
### 5.4 인증 전제
공식 문서 원문:
> "Headless mode uses your cached credentials. Authenticate once with an interactive `agy` session first."
---
## 6. 출력 포맷 3종 상세
### 6.1 `text` (기본)
```bash
agy -p "In one sentence, what does the command git bisect do?"
```
응답 본문만 평문으로 나온다. 배치에서는 오류 판별이 어려우므로 **사용하지 않는다.**
### 6.2 `json` — 단일 봉투 (**이 프로젝트의 기본 선택**)
```bash
agy -p "In one sentence, what is a git rebase?" --output-format json | jq
```
봉투 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
| `conversation_id` | string | 대화 ID (재개용) |
| `status` | string | `SUCCESS` \| `ERROR` \| `CANCELED` \| `INTERRUPTED` \| `INVALID` \| `WAITING` \| `RUNNING` |
| `response` | string | 응답 본문 |
| `error` | string | 실패 시에만 존재 |
| `duration_seconds` | number | 소요 시간 |
| `num_turns` | number | 턴 수 |
| `usage` | object | `input_tokens`, `output_tokens`, `thinking_tokens`, `cache_read_tokens`, `total_tokens` |
| `structured_output` | object | `--json-schema` 사용 시 (⚠️ 6.3/7 절 참조 — 실측에서 누락됨) |
| `json_schema` | object | 적용된 스키마 에코백 |
`status` 값 의미:
| 값 | 의미 |
|---|---|
| `SUCCESS` | 응답과 함께 정상 완료 |
| `ERROR` | 오류로 종료 |
| `CANCELED` | 취소됨 |
| `INTERRUPTED` | 인터럽트(예: SIGINT) |
| `INVALID` | 유효하지 않은 상태에 도달 |
| `WAITING` | 입력 대기 중 종료 |
| `RUNNING` | 종료 상태에 도달하지 못함 |
### 6.3 `stream-json` — NDJSON 이벤트 스트림
```bash
agy -p "In one sentence, what is a git rebase?" --output-format stream-json
```
이벤트 종류: `init`(1회) → `step_update`(여러 번) → `result`(1회)
`init` 이벤트 예:
```json
{"event":"init","conversation_id":"c3b66b04-872b-4fbe-a3a4-058a026ef20a","init":{"cwd":"/home/user/project","tools":["ask_permission","run_command","write_to_file","..."],"permission_mode":"request-review"}}
```
`step_update` (에이전트 응답 델타):
```json
{"event":"step_update","step_update":{"conversation_id":"c3b66b04-872b-4fbe-a3a4-058a026ef20a","step_index":3,"state":"DONE","step_type":"agent_response","text_delta":"Git rebase destructively rewrites...","duration_seconds":6.28,"usage":{"input_tokens":10302,"output_tokens":582,"thinking_tokens":551,"cache_read_tokens":8113,"total_tokens":10884}}}
```
`step_update` (도구 호출 — **감사 로그로 매우 유용**):
```json
{"event":"step_update","step_update":{"conversation_id":"edb1c8c1-50ba-4f3f-87eb-412d0e9d47c3","step_index":4,"state":"DONE","step_type":"tool","tool_name":"run_command","duration_seconds":0.07,"tool_info":{"name":"run_command","parameters":{"CommandLine":"echo hello_headless_demo"},"output":"hello_headless_demo\r\n"}}}
```
`tool_info``name`, `parameters`, `output`, 실패 시 `error` 를 담는다.
`result` 이벤트:
```json
{"event":"result","result":{"conversation_id":"...","status":"SUCCESS","response":"Git rebase...","duration_seconds":6.88,"num_turns":1,"usage":{"input_tokens":10418,"output_tokens":589,"thinking_tokens":551,"cache_read_tokens":8113,"total_tokens":11007}}}
```
`jq` 파싱 레시피:
```bash
# 응답 텍스트만 추출
agy -p "Name three popular version control systems, comma-separated." --output-format json | jq -r '.response'
# 스트리밍 텍스트 이어붙이기
agy -p "Explain merge conflicts in two sentences." --output-format stream-json \
| jq -j 'select(.event=="step_update") | .step_update.text_delta // empty'
# 토큰 사용량만
agy -p "In one sentence, what is a git rebase?" --output-format stream-json \
| jq 'select(.event=="result") | .result.usage'
```
### 6.4 stdin 스트리밍 입력 (`--input-format stream-json`)
여러 프롬프트를 한 세션에서 연속 실행한다. **`--output-format stream-json` 과 반드시 짝**이어야 한다.
```bash
printf '%s\n' \
'{"event":"user","message":{"content":"Reply with exactly the word: apple. Nothing else."}}' \
'{"event":"user","message":{"content":"What word did I ask you to reply with? Answer with just that word."}}' \
| agy --input-format stream-json --output-format stream-json
```
`content` 는 문자열 또는 텍스트 블록 리스트 둘 다 허용:
```json
{ "event": "user", "message": { "content": "Reply with exactly: banana" } }
{ "event": "user", "message": { "content": [{ "type": "text", "text": "Reply with exactly: banana" }] } }
```
**메타데이터 범위 주의**: `response` 는 현재 턴의 것이지만, `num_turns` / `usage` / `duration_seconds` 는 **세션 누적**이다.
세션 종료: stdin 을 닫으면 현재 턴 완료 후 프로세스가 종료된다. 정상 종료는 코드 `0`.
지원하지 않는 입력과 그 결과:
| 입력 | 결과 | 종료 코드 |
|---|---|---|
| 인식되지 않는 `event` 이름 | 건너뜀, stderr 에 경고 | — |
| `control_request` / `control_response` 이벤트 | `ERROR` result, 세션 종료 | `2` |
| CLI 가 처리하는 슬래시 명령(`/model`, `/usage`) | `ERROR` result, 세션 종료 | `2` |
| `event` 필드 누락 | `ERROR` result, 세션 종료 | `1` |
| 잘못된 JSON 줄 | `ERROR` result, 세션 종료 | `1` |
| `text` 외의 content block 타입 | `ERROR` result, 세션 종료 | `1` |
슬래시 명령 오류 메시지 원문:
```json
{ "event": "result", "result": { "conversation_id": "4fae3a70-409d-42a4-86ea-9de206a49ff4", "status": "ERROR", "response": "", "error": "/model is answered by the CLI itself and is unavailable with --input-format stream-json; run it as its own --print /model invocation", "duration_seconds": 0, "num_turns": 0, "usage": { "input_tokens": 0, "output_tokens": 0, "thinking_tokens": 0, "cache_read_tokens": 0, "total_tokens": 0 } } }
```
흔한 실수 표(공식 문서):
| 실수 | 왜 실패하나 | 해결 |
|---|---|---|
| `--output-format json` 또는 `text` 와 조합 | 종료 시 단일 봉투만 나와 턴이 유실됨 | `--output-format stream-json` 사용 |
| `-p` 로 프롬프트 전달 | 스트리밍 모드는 stdin 만 수신 | 프롬프트를 stdin 의 `user` 메시지로 전송 |
| `/model`, `/usage` 를 스트림에 전송 | CLI 내부 처리라 JSON 흐름이 깨짐 | 별도 `agy -p /model` 로 실행 |
| `num_turns` 를 턴별 카운트로 오해 | 세션 누적 값 | 현재 턴은 `response` 필드 사용 |
| stdout 을 프로세스 종료 후 읽으려 함 | stdin 닫을 때까지 세션 유지 | 줄 단위로 즉시 읽기 |
파이썬으로 세션을 구동하는 공식 예제:
```python
import json
import subprocess
proc = subprocess.Popen(
["agy", "--input-format", "stream-json", "--output-format", "stream-json"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True,
bufsize=1,
)
def ask(prompt):
"""Send one prompt and return the response for that turn."""
message = {"event": "user", "message": {"content": prompt}}
proc.stdin.write(json.dumps(message) + "\n")
proc.stdin.flush()
for line in proc.stdout:
event = json.loads(line)
if event["event"] == "result":
return event["result"]["response"]
first = ask("Name one popular version control system. Answer with one word.")
print(ask(f"Name a competitor to {first.strip()}. Answer with one word."))
proc.stdin.close()
proc.wait()
```
### 6.5 대화 이어가기
```bash
# 가장 최근 대화 이어가기
agy -p "Now explain your previous answer in more detail" --continue
# 특정 대화 ID 재개
agy -p "Summarize what we discussed" --conversation 055a398f-db14-4c5f-abbb-1bf03f8120a7
```
**배치에서는 `--continue` 를 쓰지 마라.** 배치는 매번 독립적이어야 하며, 최근 대화가 무엇인지 예측할 수 없다. 필요하면 `conversation_id` 를 명시적으로 저장·전달한다.
---
## 7. 구조화 출력과 그 함정
### 7.1 문서상 동작
```bash
agy -p "Parse v2.14.3 into major, minor, patch." \
--output-format json \
--json-schema '{"type":"object","properties":{"major":{"type":"integer"},"minor":{"type":"integer"},"patch":{"type":"integer"}},"required":["major","minor","patch"]}' | jq
```
스키마는 **문자열, 파일 경로, 또는 원시 타입**(string, number, integer, boolean)을 받는다. 결과는 봉투의 `structured_output` 필드에 담긴다.
### 7.2 실측 결과 — 기대와 다르다 ⚠️
명령:
```bash
agy -p "Return today's weekday name in Korean." --output-format json \
--json-schema '{"type":"object","properties":{"weekday":{"type":"string"}},"required":["weekday"]}' \
--print-timeout 90s
```
실제 출력:
```json
{"conversation_id":"cb636581-e8de-4375-8542-4f83fca1db9c","status":"SUCCESS","response":"{\"toolAction\":\"Finishing task\",\"toolSummary\":\"Return weekday in Korean\",\"weekday\":\"수요일\"}\n오늘(2026년 9월 2일)의 요일은 **수요일**입니다.\n{\"toolAction\":\"Finish task\",\"toolSummary\":\"Task completion\",\"weekday\":\"수요일\"}\n오늘의 요일은 **수요일**입니다.\n{\"toolAction\":\"Finish task\",\"toolSummary\":\"Finish task\",\"weekday\":\"수요일\"}\n오늘(2026년 9월 2일)의 요일은 **수요일**입니다.\n{\"toolAction\":\"Finish task\",\"toolSummary\":\"Finish task\",\"weekday\":\"수요일\"}\n오늘(2026-09-02)의 요일은 **수요일**입니다.\n","duration_seconds":52.9313561,"num_turns":4,"json_schema":{"type":"object","properties":{"weekday":{"type":"string"}},"required":["weekday"]},"usage":{"input_tokens":86038,"output_tokens":2378,"thinking_tokens":2169,"cache_read_tokens":163370,"total_tokens":88416}}
```
**관찰된 문제**:
| 증상 | 상세 |
|---|---|
| `structured_output` 필드 부재 | 봉투에 `json_schema` 는 에코백됐지만 `structured_output` 이 없다 |
| `response` 오염 | JSON 조각과 한국어 산문이 뒤섞여 있다 |
| 4회 반복 | `num_turns: 4` — 모델이 스키마를 만족시키려 4번 시도했다 |
| 스키마에 없는 필드 | `toolAction`, `toolSummary` 가 섞여 나왔다 (내부 도구 래퍼로 추정) |
| 비용 폭증 | input 86k, output 2.4k, 총 88k 토큰, 52.9초. 같은 답을 얻는 단순 호출의 3배 |
**설계 결론 (이 프로젝트의 규칙)**:
1. `--json-schema`**1차 수단으로 신뢰하지 않는다.**
2. 프롬프트 자체에 출력 형식을 강하게 못박는다: "오직 JSON 객체 하나만 출력하고, 코드 펜스·설명·서문을 붙이지 마라."
3. 파이프라인은 **`response` 에서 JSON 을 견고하게 추출**한다: 코드 펜스 제거 → 첫 `{` 부터 균형 잡힌 마지막 `}` 까지 슬라이스 → `json.loads`**jsonschema 로 자체 검증**.
4. 검증 실패 시 최대 N회 재시도하고, 그래도 실패하면 **AI 결과 없이 리포트를 생성**한다(graceful degradation).
5. `--json-schema` 를 쓰더라도 **보조 수단**으로만 병행하고, `structured_output` 이 있으면 우선 사용하되 없으면 4번 경로로 폴백한다.
권장 추출 함수:
```python
import json, re
_FENCE = re.compile(r"^\s*```(?:json)?\s*|\s*```\s*$", re.MULTILINE)
def extract_json_object(text: str) -> dict | None:
"""agy 의 response 문자열에서 첫 번째 완결 JSON 객체를 견고하게 추출한다."""
if not text:
return None
s = _FENCE.sub("", text)
start = s.find("{")
while start != -1:
depth, in_str, esc = 0, False, False
for i in range(start, len(s)):
ch = s[i]
if in_str:
if esc:
esc = False
elif ch == "\\":
esc = True
elif ch == '"':
in_str = False
continue
if ch == '"':
in_str = True
elif ch == "{":
depth += 1
elif ch == "}":
depth -= 1
if depth == 0:
try:
return json.loads(s[start:i + 1])
except json.JSONDecodeError:
break
start = s.find("{", start + 1)
return None
```
---
## 8. 모델 · 에이전트 · 추론 강도
### 8.1 모델 목록 (실측 `agy models`, 2026-09-02)
| 슬러그 | 표시 이름 |
|---|---|
| `gemini-3.7-flash-high` | Gemini 3.7 Flash (High) |
| `gemini-3.7-flash-medium` | Gemini 3.7 Flash (Medium) |
| `gemini-3.7-flash-low` | Gemini 3.7 Flash (Low) |
| `gemini-3.6-flash-high` | Gemini 3.6 Flash (High) |
| `gemini-3.6-flash-medium` | Gemini 3.6 Flash (Medium) |
| `gemini-3.6-flash-low` | Gemini 3.6 Flash (Low) |
| `gemini-3.1-pro-high` | Gemini 3.1 Pro (High) |
| `gemini-3.1-pro-low` | Gemini 3.1 Pro (Low) |
| `claude-sonnet-4-6` | Claude Sonnet 4.6 (Thinking) |
| `claude-opus-4-6-thinking` | Claude Opus 4.6 (Thinking) |
| `gpt-oss-120b-medium` | GPT-OSS 120B (Medium) |
> 참고: 공식 문서 예시에는 `gemini-3.5-flash-medium` 도 등장하지만 실측 목록에는 없었다. **모델 슬러그는 하드코딩하지 말고, 부트스트랩 때 `agy models` 로 검증**하는 것이 안전하다.
```bash
# 모델 고정
agy -p "Reverse the string antigravity." --model gemini-3.5-flash-medium
# 추론 강도
agy -p "Outline a plan to add caching to this service." --effort high
# 에이전트 선택
agy -p "Review this function for edge cases." --agent <agent-name>
```
알 수 없는 모델을 주면 종료 코드 비0 + `status: ERROR`:
```bash
agy -p "hi" --model does-not-exist-model --output-format json; echo "exit=$?"
```
```json
{"conversation_id":"","status":"ERROR","response":"","error":"invalid model selection (--model \"does-not-exist-model\" --effort \"\"): model does-not-exist-model is not recognized as a known model or custom model in settings\nAvailable models:\n Gemini 3.6 Flash (High)\n ...","duration_seconds":0,"num_turns":0,"usage":{...}}
```
```
exit=1
```
### 8.2 이 프로젝트의 모델 선택 지침
| 유스케이스 | 권장 모델 | 이유 |
|---|---|---|
| 일일 변경사항 한국어 요약 | `gemini-3.7-flash-medium` | 정형 입력의 요약, 속도·비용 우위 |
| 셀렉터 자가 복구 제안 | `gemini-3.1-pro-high` 또는 `claude-sonnet-4-6` | HTML 구조 추론은 난도가 높음 |
| 이상 탐지 해석 | `gemini-3.7-flash-high` | 짧은 추론 |
| 주간 트렌드 코멘터리 | `gemini-3.1-pro-high` | 종합·서술 품질 |
### 8.3 에이전트 (실측 `agy agents`)
```
flutter_a11y_agent
```
현재 머신에는 커스텀 에이전트가 하나 등록돼 있다. 이 프로젝트는 커스텀 에이전트를 만들지 않고 기본 에이전트 + 프롬프트 파일로 간다(단순성 우선).
---
## 9. 권한 모델
### 9.1 세밀 권한 엔진
리소스 표기는 `action(target)` 형식이며 세 목록으로 통제한다.
| 목록 | 동작 |
|---|---|
| `deny` | 즉시 차단 |
| `ask` | 명시적 승인 대기 |
| `allow` | 프롬프트 없이 자동 승인 |
**우선순위: Deny > Ask > Allow** (충돌 시 엄격하게 이 순서로 평가)
### 9.2 지원 액션과 타깃
| 액션 | 형식 | 동작 |
|---|---|---|
| `read_file` | 경로 또는 `*` | 절대 경로 또는 워크스페이스 루트 기준 상대 경로 매칭. **재귀적** 읽기 권한 부여 |
| `write_file` | 경로 또는 `*` | 같은 타깃에 대해 `read_file`**암묵적으로 함께 부여** |
| `read_url` | 도메인 또는 `*` | 호스트명과 서브도메인 매칭 (예: `google.com``mail.google.com` 포함) |
| `execute_url` | 도메인 또는 `*` | 웹 액추에이션(클릭, 타이핑) |
| `command` | 접두사/정규식 또는 `*` | **공백으로 분리된 각 토큰이 앵커된 정규식으로 평가됨** |
| `unsandboxed` | 접두사 또는 `*` | 컨테이너 격리 밖에서 명령 실행 |
| `mcp` | `server/tool` 또는 `*` | MCP 도구 접근 |
암묵 규칙:
- 파일 쓰기 권한은 그 경로의 읽기 권한을 자동 부여
- 경로에 읽기를 거부하면 그 경로 쓰기도 차단
기본 동작:
1. **워크스페이스 자동 허용**: 활성 프로젝트 디렉터리 안의 파일 읽기·쓰기는 자동 허용
2. **웹은 기본 ask**
3. **설정되지 않은 액션은 기본 ask**
### 9.3 헤드리스에서의 권한
공식 문서 원문 요지: 헤드리스에는 대화형 프롬프트가 없고 정책으로 처리된다. **워크스페이스 파일 읽기/쓰기는 자동 허용되지만, 셸 명령은 기본 Ask 이며 권한이 없으면 soft-deny 된다.**
이것이 배치에서 가장 흔한 실패 원인이다. 반드시 사전 허용 규칙을 넣어라.
설정 예시 (`~/.gemini/antigravity-cli/settings.json`):
```json
{
"permissions": {
"allow": [
"command(git)",
"command(npm run (build|lint|test))",
"read_url(google.com)",
"mcp(linter/*)",
"write_file(src/)"
],
"deny": [
"command(sudo)",
"write_file(.git/)"
],
"ask": ["command(*)", "mcp(sql/execute_mutation)"]
}
}
```
전부 자동 승인(위험):
```bash
agy -p "Run the test suite and report failures" --dangerously-skip-permissions
```
공식 경고 원문:
> "`--dangerously-skip-permissions` approves all tool calls, including file writes and command execution. Prefer scoped `permissions.allow` rules unless you fully trust the prompt and environment."
**이 프로젝트의 결론**: `--dangerously-skip-permissions`**쓰지 않는다.** 이유는 크롤링한 외부 텍스트가 프롬프트에 들어가므로 프롬프트 인젝션 표면이 존재하기 때문이다. 대신 이 프로젝트 전용 `permissions.allow` 를 최소 범위로 설정한다. 상세는 `docs/research/10-agy-agent-integration-patterns.md`.
### 9.4 크로스 플랫폼 경로 정규화
공식 문서 원문:
> "Antigravity automatically normalizes paths prior to rule evaluation by stripping drive letters (e.g., C:) and converting all backslashes (\) to forward slashes (/)."
**즉 규칙에 `D:\workspace\DMF_Crawler` 대신 `workspace/DMF_Crawler` 형태를 쓴다.** 드라이브 문자는 제거되고 슬래시로 변환된다.
### 9.5 대화형 프롬프트에서의 범위 확장
승인 카드에서 타깃 문자열을 직접 편집해 권한 범위를 넓힐 수 있다. CLI 가 편집을 검증하고 그 세션 동안 확장된 권한을 적용한다.
---
## 10. 실행 모드
| 모드 | 동작 |
|---|---|
| `default` | 파일 수정·생성 전에 대화형 diff 리뷰로 일시정지 |
| `accept-edits` | 파일 편집·생성(`mkdir`, `touch`, 파일 쓰기)을 자동 승인 |
| `plan` | `/plan` 지시 접두사를 붙여 코드 작성 전 단계 분석·개요 작성 |
`accept-edits` 에서는 `write_to_file`, `replace_file_content`, `multi_replace_file_content` 가 자동 실행되며, **세션 중 생성된 서브에이전트도 이 설정을 상속**한다.
설정 방법:
```bash
agy --mode=accept-edits
agy --mode=plan
```
```json
{"agentMode": "accept-edits"}
```
대화형에서는 `/settings` 로 기본 모드를 고르거나 `Shift+Tab` 으로 순환한다.
**권한과 모드는 독립적이다.** 공식 문서 원문:
> "Tool permission rules configured via `/permissions` or `--dangerously-skip-permissions` continue to govern shell commands (`run_command`) across all execution modes."
즉 모드는 **파일 작업**을, 권한은 **셸 명령**을 각각 통제한다.
---
## 11. 설정 파일 전체 키
### 11.1 위치
| 파일 | 경로 |
|---|---|
| 설정 | `~/.gemini/antigravity-cli/settings.json` (Windows 도 `%USERPROFILE%\.gemini\antigravity-cli\settings.json`) |
| 키바인딩 | `~/.gemini/antigravity-cli/keybindings.json` |
**희소 저장(sparse persistence)**: 기본값과 다른 값만 디스크에 기록한다.
**프로젝트 단위 설정은 문서화돼 있지 않다** — 사용자 단위 설정만 존재. 프로젝트별 차이는 CLI 플래그로 준다.
### 11.2 전체 키 표
| 키 | 타입 | 기본값 | 값/설명 |
|---|---|---|---|
| `colorScheme` | string | `"terminal"` | `"light"`, `"solarized light"`, `"colorblind-friendly light"`, `"dark"`, `"solarized dark"`, `"colorblind-friendly dark"`, `"tokyo night"`, `"terminal"` |
| `altScreenMode` | string | `"default"` | `"default"`(적응형), `"always"`(대체 화면 버퍼 강제), `"never"`(인라인 강제) |
| `toolPermission` | string | `"request-review"` | `"request-review"`, `"proceed-in-sandbox"`, `"always-proceed"`, `"strict"` |
| `artifactReviewPolicy` | string | `"asks-for-review"` | `"asks-for-review"`, `"agent-decides"`, `"always-proceed"` |
| `notifications` | boolean | `false` | 작업 완료 시 시스템 데스크톱 알림 및 터미널 벨 |
| `showTips` | boolean | `true` | 생성 중 프롬프트 패널 위에 팁 표시 |
| `showFeedbackSurvey` | boolean | `true` | 주기적 품질 피드백 설문 표시 |
| `editor` | string | `"auto"` | `"auto"`, `"vim"`, `"emacs"`, 커스텀 |
| `editorMode` | string | `"default"` | `"default"`(평문 편집), `"vim"`(모달 편집) |
| `vimInsertFirst` | boolean | `false` | Vim 편집을 Insert 모드로 시작하고 `Enter` 로 제출 |
| `allowNonWorkspaceAccess` | boolean | `false` | 에이전트 파일 도구가 Git/워크스페이스 루트 밖으로 나갈 수 있게 허용 |
| `enableTerminalSandbox` | boolean | `false` | 에이전트가 실행하는 로컬 명령을 OS 격리 링으로 제한 |
| `useG1Credits` | boolean | `false` | 플랜 쿼터 소진 후 개인 AI 크레딧 사용 |
| `enableTelemetry` | boolean | `true` | 메트릭 수집·크래시 로그 전송 허용 |
| `verbosity` | string | `"high"` | `"high"`(전체 사고·출력), `"low"`(최소 표시) |
| `runningLightSpeed` | string | `"medium"` | `"fast"`, `"medium"`, `"slow"`, `"off"` |
| `agentMode` | string | `"default"` | `"accept-edits"`, `"plan"` (모드 문서 기준) |
| `permissions` | object | — | `allow` / `deny` / `ask` 배열 (9절 참조) |
| `statusLine` | object | — | `{"type":"command","command":"<경로>"}` (실측 확인) |
| `trustedWorkspaces` | array | — | 신뢰 워크스페이스 경로 목록 (실측 확인) |
| `modelProvider` | string | — | `"gemini"` 로 설정 시 `GEMINI_API_KEY` 사용, 로그인 화면 건너뜀 (설치 문서 기준) |
| `model` | string | — | 실측 파일에는 슬러그가 아니라 **표시 이름**(`"Gemini 3.7 Flash (High)"`)으로 저장돼 있었다 |
문서 예시:
```json
{
"colorScheme": "tokyo night",
"altScreenMode": "always",
"toolPermission": "request-review",
"notifications": true,
"enableTerminalSandbox": true
}
```
### 11.3 `toolPermission` 값 의미 (베스트 프랙티스 문서)
| 값 | 동작 |
|---|---|
| `request-review` (기본) | 쓰기 작업, bash 명령, 원격 네트워크 호출 전에 매번 확인 |
| `proceed-in-sandbox` | 모든 터미널 실행을 보안 샌드박스 격리 링으로 제한. 안전한 명령은 자율 실행, 위험한 명령은 리뷰 요청 |
| `always-proceed` | 자동 진행 |
| `strict` | 모든 비읽기 작업에 대해 항상 확인, 완전한 라인별 투명성 |
### 11.4 환경변수
| 변수 | 용도 |
|---|---|
| `GEMINI_API_KEY` | `modelProvider: "gemini"` 와 함께 사용하는 API 키 |
| `GOOGLE_GEMINI_BASE_URL` | 대체 Gemini 호환 엔드포인트 |
| `AGY_CLI_DISABLE_AUTO_UPDATE` | `true` 로 자동 업데이트 비활성화 |
| `LOCALAPPDATA` | Windows 설치 경로 결정에 사용 |
| `PROCESSOR_ARCHITEW6432` / `PROCESSOR_ARCHITECTURE` | 설치 스크립트의 아키텍처 감지 |
> ⚠️ 공식 설정 문서는 "환경변수로 설정을 덮어쓰는 방법은 문서화돼 있지 않다"고 명시한다. 런타임 재정의는 CLI 플래그로만 한다.
---
## 12. 디렉터리·파일 레이아웃 실측
`~/.gemini/antigravity-cli/` 실측 목록:
| 항목 | 종류 | 설명 |
|---|---|---|
| `settings.json` | 파일 | 사용자 설정 |
| `keybindings.json` | 파일 | 키바인딩(문서 기준) |
| `antigravity-oauth-token` | 파일 | **OAuth 토큰 평문 JSON (보안 주의)** |
| `installation_id` | 파일 | 설치 식별자 |
| `history.jsonl` | 파일 | 프롬프트 이력 (실측 213 KB) |
| `conversation_summaries.db` | 파일 | 대화 요약 SQLite (실측 140 KB) |
| `conversations/` | 디렉터리 | 대화 저장소 |
| `log/` | 디렉터리 | 로그 (`cli-YYYYMMDD_HHMMSS.log` 형식) |
| `cli.log` | 심볼릭 링크 | 최신 로그 파일로의 링크 |
| `crashes/` | 디렉터리 | 크래시 덤프 |
| `cache/` | 디렉터리 | 캐시 |
| `brain/` | 디렉터리 | 세션별 작업 공간 (하위에 `scratch/`) |
| `knowledge/` | 디렉터리 | 지식 저장소 |
| `rules/` | 디렉터리 | 규칙 파일 |
| `skills/` | 디렉터리 | 스킬 |
| `mcp/` | 디렉터리 | MCP 설정 |
| `plugins`/`bin`/`builtin`/`implicit`/`presence`/`annotations` | 디렉터리 | 내부 |
| `updater/` | 디렉터리 | 자동 업데이터 (`update.lock` 포함) |
| `last_check.timestamp` | 파일 | 업데이트 확인 시각 |
| `statusline.cmd`, `statusline.py` | 파일 | 커스텀 상태줄 (사용자 설정) |
**로그 경로 규약**: 실행마다 새 로그 파일이 생기고 `cli.log` 가 최신을 가리킨다. `--log-file` 로 재정의 가능 → **배치에서는 실행 ID별 로그 파일을 지정**하는 것을 권장한다.
---
## 13. 종료 코드와 오류 처리
| 코드 | 의미 |
|---|---|
| `0` | 성공 |
| `1` | 일반 실패 (잘못된 모델, 잘못된 JSON 입력, event 필드 누락, 잘못된 content block 타입) |
| `2` | 스트림 입력에서 `control_request`/`control_response` 또는 CLI 처리 슬래시 명령 수신 |
| 비0 (기타) | 실패, 사유는 stderr 로 |
`json` / `stream-json` 모드에서는 실패가 `status``error` 필드에도 나타난다. **배치는 종료 코드와 `status` 를 둘 다 확인**해야 한다.
CI 실행 공식 예제:
```bash
#!/usr/bin/env bash
set -euo pipefail
result=$(agy -p "Name three popular version control systems, comma-separated." \
--output-format json \
--print-timeout 10m)
status=$(echo "$result" | jq -r '.status')
if [[ "$status" != "SUCCESS" ]]; then
echo "Agent run failed: $(echo "$result" | jq -r '.error')" >&2
exit 1
fi
echo "$result" | jq -r '.response' > result.txt
```
타임아웃 늘리기:
```bash
agy -p "Summarize the design tradeoffs of optimistic locking." --print-timeout 15m
```
---
## 14. 크레딧·쿼터
| 항목 | 내용 |
|---|---|
| 상태줄 표시 | 남은 크레딧 표시 (예: `AI Credits: 42`) |
| `/credits` | 상세 통계 패널, 구매 링크 |
| `/usage` (별칭 `/quota`) | 모델별 API 쿼터 조회 |
| `useG1Credits` 설정 | `true` 면 플랜 쿼터 소진 시 개인 크레딧으로 폴백, `false` 면 차단 |
> ⚠️ 공식 크레딧 문서에는 **무료/유료 티어 구분, 일일·주간 한도, 모델별 단가, 쿼터 소진 시 오류 메시지·종료 동작, `GEMINI_API_KEY` 과금 차이, 레이트 리밋, 자동화 사용 가이드가 없다.** `/docs/plans` 페이지를 참조하라고만 안내한다. → 부록 B 미해결 항목.
**배치 관점 대응**: `usage.total_tokens` 를 매 호출 로깅하고, 일일 누적 상한을 파이프라인 자체에서 강제한다. 쿼터 소진은 `status: ERROR` + `error` 문자열로 감지하고, 감지되면 **AI 단계를 건너뛰고 리포트를 생성**한다.
---
## 15. 트러블슈팅
| 증상 | 오류 문자열 | 해결 |
|---|---|---|
| 명령을 찾을 수 없음 | `bash: agy: command not found` | `~/.local/bin` 을 PATH 에 추가(`~/.bashrc`/`~/.zshrc`), Windows 는 레지스트리 PATH 등록. **배치는 절대 경로 사용으로 회피** |
| 키링 접근 실패 | `Error: failed to retrieve token: secret keyring is locked` | 키체인 잠금 해제, 앱 권한 확인, 헤드리스 Linux 는 D-Bus 세션 초기화 |
| SSH 클립보드 실패 | `Error: local pasteboard is empty or unreachable over SSH connection` | iTerm2/Ghostty 사용, OSC 52 쓰기 채널 활성화, tmux 클립보드 설정 |
| 업데이터 락 | `Warning: another background updater process is already active (update.lock)` | `~/.gemini/antigravity-cli/updater/update.lock` 삭제 또는 `AGY_CLI_DISABLE_AUTO_UPDATE=true` |
| 설치 디렉터리 권한 | — | `~/.local/bin/`(Unix) 또는 `%LOCALAPPDATA%\agy\bin`(Windows) 소유권·쓰기 권한 확인 |
| 체크섬 실패 | `Security Halt: Checksum verification failed.` | 네트워크·프록시 문제 또는 변조. 재시도, 실패 시 중단 |
| 알 수 없는 모델 | `invalid model selection (--model "..." --effort "")` | `agy models` 로 슬러그 확인 |
---
## 16. 배치·스케줄러 관점 체크리스트
이 프로젝트가 06:00 무인 실행에서 반드시 지켜야 할 규칙.
- [ ] **절대 경로로 호출한다.** `%LOCALAPPDATA%\agy\bin\agy.exe` — 스케줄러 세션의 PATH 를 신뢰하지 않는다.
- [ ] **작업은 로그인한 사용자 계정으로 실행한다.** SYSTEM 계정은 `~/.gemini/antigravity-cli/antigravity-oauth-token` 에 접근할 수 없다.
- [ ] **`AGY_CLI_DISABLE_AUTO_UPDATE=true`** 를 작업 환경에 설정해 실행 중 자동 업데이트를 막는다.
- [ ] **`--output-format json`** 을 쓰고 `stdout` 만 파싱한다. `stderr` 는 로그로 분리 저장한다.
- [ ] **종료 코드와 `status` 를 둘 다 검사**한다.
- [ ] **`--print-timeout` 을 명시**한다. 기본 5분은 긴 요약 작업에 부족할 수 있다. 권장 `10m`.
- [ ] **`--json-schema` 결과를 맹신하지 않는다.** 자체 JSON 추출 + jsonschema 검증을 반드시 통과시킨다.
- [ ] **`--continue` 를 쓰지 않는다.** 매 실행은 독립적이어야 한다.
- [ ] **`--dangerously-skip-permissions` 를 쓰지 않는다.** 대신 `permissions.allow` 를 최소 범위로 설정한다.
- [ ] **`--disable-slash-commands`** 를 붙여, 크롤링한 텍스트에 우연히 들어간 `/명령` 이 확장되는 것을 막는다.
- [ ] **호출 횟수를 최소화한다.** 첫 호출 오버헤드가 input 28k 토큰이다. 여러 질문을 한 프롬프트로 묶는다.
- [ ] **`usage` 를 매번 로깅**하고 일일 토큰 상한을 코드로 강제한다.
- [ ] **`--log-file` 로 실행 ID별 로그 경로를 지정**한다.
- [ ] **AI 실패가 배치 실패가 되지 않게 한다.** agy 가 죽어도 xlsx 리포트는 생성돼야 한다.
- [ ] **미인증 감지 시 사용자에게 알린다.** 토스트 알림 + 재로그인용 콘솔 창 띄우기 경로를 준비한다.
권장 호출 형태(PowerShell):
```powershell
$agy = Join-Path $env:LOCALAPPDATA 'agy\bin\agy.exe'
$env:AGY_CLI_DISABLE_AUTO_UPDATE = 'true'
$runId = (Get-Date -Format 'yyyyMMdd_HHmmss')
$logDir = 'D:\workspace\DMF_Crawler\logs'
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$stdout = Join-Path $logDir "agy_$runId.stdout.json"
$stderr = Join-Path $logDir "agy_$runId.stderr.log"
$args = @(
'-p', (Get-Content -Raw 'D:\workspace\DMF_Crawler\prompts\daily_summary.md'),
'--output-format', 'json',
'--model', 'gemini-3.7-flash-medium',
'--effort', 'medium',
'--print-timeout', '10m',
'--disable-slash-commands',
'--log-file', (Join-Path $logDir "agy_cli_$runId.log")
)
$proc = Start-Process -FilePath $agy -ArgumentList $args -NoNewWindow -Wait -PassThru `
-RedirectStandardOutput $stdout -RedirectStandardError $stderr
if ($proc.ExitCode -ne 0) { throw "agy exited with $($proc.ExitCode). See $stderr" }
$envelope = Get-Content -Raw $stdout | ConvertFrom-Json
if ($envelope.status -ne 'SUCCESS') { throw "agy status=$($envelope.status): $($envelope.error)" }
```
---
## 17. 주변 생태계
npm 레지스트리 검색 결과 — `agy` 를 감싸는 서드파티 어댑터가 활발하다. 이 프로젝트가 직접 쓰지는 않지만, **호출 규약의 참고 구현**으로 가치가 있다.
| 패키지 | 설명 | 최근 게시 |
|---|---|---|
| `@claudexor/harness-agy` | "Google Antigravity CLI adapter (`agy -p --output-format stream-json`)" — **stream-json 어댑터 참고 구현** | 2026-09-01 |
| `@aibridge/driver-agy` | Antigravity (agy) CLI driver for aibridge | 2026-08-22 |
| `dsh-agy-provider` | 로컬 인증된 AGY CLI 를 DSH 모델 프로바이더로 노출 | 2026-08-24 |
| `dsh-agy-link` | thinking, tool activity, token usage 포함 연동 | 2026-08-28 |
| `dsh-agy` | OAuth 인증 + 멀티 계정 풀 | 2026-08-22 |
| `agy-auth` | AGY CLI 멀티 계정 관리 도구 | 2026-08-12 |
| `agy-multi` | 멀티 Google 계정 OAuth (login, quota, rotate) | 2026-08-13 |
| `@anthonyhaussman/opencode-agy-auth` | OpenCode 인증 플러그인 (OAuth, quota, 모델 조회) | 2026-09-02 |
| `opencode-agy-plugin` | OpenCode 프롬프트를 agy 로 라우팅 | 2026-08-15 |
| `agy-pi`, `@bacnh85/pi-agy`, `@tian.zuo/pi-antigravity` | Pi 코딩 에이전트 브리지 | 2026-08 |
| `agy-plugin-cc` | Claude Code 에서 agy 를 쓰는 플러그인 설치기 | 2026-08-26 |
| `n8n-nodes-agy` | **n8n 커뮤니티 노드 — agy 명령 실행 통합** (워크플로 자동화 참고) | 2026-08-18 |
| `@bash0816/agy-termux` | Termux(Android ARM64) 용 agy | 2026-09-01 |
| `linkgravity` | Discord/Telegram 봇 브리지 (음성 지원) | 2026-08-26 |
**시사점**: 멀티 계정 로테이션 도구(`agy-auth`, `agy-multi`, `dsh-agy`)가 여럿 존재한다는 것은 **쿼터 제약이 실제로 존재하고 사용자들이 부딪히고 있다**는 신호다. 이 프로젝트의 일일 토큰 상한 설계는 필수다.
로컬에 등록된 MCP 서버 실측:
```
NAME TYPE STATUS COMMAND/URL
chrome-devtools stdio enabled npx -y chrome-devtools-mcp@latest --browser-url=http://localhost:9222
data-agent-kit stdio enabled node ...\mcp_proxy_bundle.js dataAgentKit-antigravityide
haramlog-ops stdio enabled node d:\workspace\HaramLog\tools\mcp\haramlog-ops-mcp.mjs
notebooks stdio enabled node ...\mcp_proxy_bundle.js notebooks-antigravityide
unityMCP http enabled http://localhost:8080/mcp
visualization stdio enabled node ...\mcp_proxy_bundle.js visualization-antigravityide
```
> ⚠️ **배치 관점 위험**: 등록된 MCP 서버는 `agy` 실행 시 함께 기동을 시도한다. `chrome-devtools` 는 `npx` 를 호출하고 `unityMCP` 는 localhost:8080 을 찾는다. 무인 배치에서 이들이 없으면 기동 지연이나 오류가 날 수 있다. **배치 전용으로 MCP 를 비활성화**하는 방안을 검토하라 (`agy mcp disable <name>`). → 부록 B.
---
## 부록 A. 출처 목록
| 제목 | URL | 확인 |
|---|---|---|
| Antigravity CLI — Overview | https://antigravity.google/docs/cli/overview | ✅ 직접 열람 |
| Antigravity CLI — Getting Started | https://antigravity.google/docs/cli/getting-started | ✅ 직접 열람 |
| Antigravity CLI — Installation & Auth | https://antigravity.google/docs/cli/install | ✅ 직접 열람 |
| Antigravity CLI — Headless | https://antigravity.google/docs/cli/headless | ✅ 직접 열람 (핵심 출처) |
| Antigravity CLI — Reference | https://antigravity.google/docs/cli/reference | ✅ 직접 열람 |
| Antigravity CLI — Settings | https://antigravity.google/docs/cli/settings | ✅ 직접 열람 |
| Antigravity CLI — Permissions | https://antigravity.google/docs/cli/permissions | ✅ 직접 열람 |
| Antigravity CLI — Modes | https://antigravity.google/docs/cli/modes | ✅ 직접 열람 |
| Antigravity CLI — Credits | https://antigravity.google/docs/cli/credits | ✅ 직접 열람 |
| Antigravity CLI — Troubleshooting | https://antigravity.google/docs/cli/troubleshooting | ✅ 직접 열람 |
| Antigravity CLI — Best Practices | https://antigravity.google/docs/cli/best-practices | ✅ 직접 열람 |
| Windows 설치 스크립트 (전문) | https://antigravity.google/cli/install.ps1 | ✅ 전문 확인 |
| macOS/Linux 설치 스크립트 | https://antigravity.google/cli/install.sh | 미열람 |
| Windows CMD 설치 스크립트 | https://antigravity.google/cli/install.cmd | 미열람 |
| 자동 업데이터 매니페스트 엔드포인트 | https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/windows_amd64.json | install.ps1 에서 확인 |
| npm 레지스트리 검색 `agy` | https://registry.npmjs.org/-/v1/search?text=agy&size=20 | ✅ 직접 열람 |
| 한국어 튜토리얼 — Antigravity CLI (agy) | https://antigravity.todaycode.kr/antigravity-cli.html | 부분 열람 (IDE 중심) |
| 한국어 블로그 — Antigravity CLI 기초·마이그레이션 | https://goddaehee.tistory.com/ | 검색 결과로만 확인 |
| 한국어 블로그 — agy 명령어 총정리 | https://infotake.tistory.com/ | 검색 결과로만 확인 |
| 참조 문서 (미열람) — 마이그레이션 | https://antigravity.google/docs/cli/gcli-migration | 미열람 |
| 참조 문서 (미열람) — 서브에이전트 | https://antigravity.google/docs/cli/subagents | 미열람 |
| 참조 문서 (미열람) — 샌드박스 | https://antigravity.google/docs/cli/sandbox | 미열람 |
| 참조 문서 (미열람) — MCP | https://antigravity.google/docs/cli/mcp | 미열람 |
| 참조 문서 (미열람) — 플러그인 | https://antigravity.google/docs/cli/plugins | 미열람 |
| 참조 문서 (미열람) — 프로젝트 | https://antigravity.google/docs/cli/projects | 미열람 |
| 참조 문서 (미열람) — 대화 | https://antigravity.google/docs/cli/conversations | 미열람 |
| 참조 문서 (미열람) — 아티팩트 | https://antigravity.google/docs/cli/artifacts | 미열람 |
| 참조 문서 (미열람) — 플랜/쿼터 | https://antigravity.google/docs/plans | 미열람 |
전체 문서 사이드바 목록(향후 참조용): `overview`, `getting-started`, `install`, `tutorial`, `using`, `features`, `gcli-migration`, `prompting`, `artifacts`, `conversations`, `modes`, `headless`, `subagents`, `sandbox`, `permissions`, `projects`, `settings`, `vim-editor-mode`, `credits`, `mcp`, `plugins`, `statusline`, `title`, `commands/agents`, `commands/codesearch`, `commands/credits`, `commands/diff`, `commands/permissions`, `commands/resume`, `commands/statusline`, `commands/title`, `commands/usage`, `commands/voice`, `best-practices`, `troubleshooting`, `reference`
---
## 부록 B. 미해결 질문 / 실측 필요 항목
- [ ] `--json-schema``structured_output` 필드가 어떤 조건에서 채워지는가? 모델별 차이인가, 프롬프트 형식 문제인가? (실측에서 누락됨)
- [ ] 쿼터 소진 시 정확한 `error` 문자열과 종료 코드는? 감지 로직을 정확히 쓰려면 필요하다. `/docs/plans` 확인 필요.
- [ ] `agy update` 의 정확한 동작과 종료 코드. 실행 중 다른 인스턴스가 있으면 어떻게 되는가?
- [ ] winget 이 작업 스케줄러(사용자 계정 / SYSTEM 계정)에서 동작하는가?
- [ ] `modelProvider: "gemini"` + `GEMINI_API_KEY` 경로에서 사용 가능한 모델 목록이 OAuth 경로와 다른가? Claude 모델도 쓸 수 있는가?
- [ ] 등록된 MCP 서버를 배치 실행에서만 비활성화하는 방법이 있는가? (프로젝트 단위 설정이 없다면 전역 비활성화뿐인가)
- [ ] `--project` / `--new-project` 의 정확한 의미와 배치에서의 활용 가치.
- [ ] `--sandbox` 가 Windows 에서 실제로 무엇을 제한하는가?
- [ ] 첫 호출 input_tokens 28k 의 구성 — 시스템 프롬프트인가 워크스페이스 인덱싱인가? 빈 디렉터리에서 실행하면 줄어드는가? (배치 비용 최적화 직결)
- [ ] `--add-dir` 로 워크스페이스를 좁히면 토큰 사용량이 감소하는가?
- [ ] OAuth access_token 만료 주기와 자동 갱신 실패 시 정확한 오류 표면.
- [ ] `conversations/``history.jsonl` 이 무한히 커지는가? 배치 장기 운영 시 정리 정책 필요.
---
*이 문서는 프로젝트의 SSOT 다. 새로운 사실을 확인하면 여기를 갱신하고, 다른 문서는 여기를 링크로 참조한다.*