mim-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. mim_cli-0.1.0/.env.sample +65 -0
  2. mim_cli-0.1.0/.github/workflows/publish.yml +42 -0
  3. mim_cli-0.1.0/.gitignore +40 -0
  4. mim_cli-0.1.0/CLAUDE.md +87 -0
  5. mim_cli-0.1.0/PKG-INFO +14 -0
  6. mim_cli-0.1.0/README.md +175 -0
  7. mim_cli-0.1.0/docs/superpowers/plans/2026-04-11-meme-cli.md +2227 -0
  8. mim_cli-0.1.0/pyproject.toml +29 -0
  9. mim_cli-0.1.0/src/mim_cli/__init__.py +0 -0
  10. mim_cli-0.1.0/src/mim_cli/ai.py +98 -0
  11. mim_cli-0.1.0/src/mim_cli/cli.py +830 -0
  12. mim_cli-0.1.0/src/mim_cli/config.py +28 -0
  13. mim_cli-0.1.0/src/mim_cli/embeddings.py +65 -0
  14. mim_cli-0.1.0/src/mim_cli/models.py +49 -0
  15. mim_cli-0.1.0/src/mim_cli/net.py +114 -0
  16. mim_cli-0.1.0/src/mim_cli/output.py +209 -0
  17. mim_cli-0.1.0/src/mim_cli/providers/__init__.py +94 -0
  18. mim_cli-0.1.0/src/mim_cli/providers/fetch/__init__.py +1 -0
  19. mim_cli-0.1.0/src/mim_cli/providers/fetch/giphy.py +88 -0
  20. mim_cli-0.1.0/src/mim_cli/providers/fetch/openverse.py +70 -0
  21. mim_cli-0.1.0/src/mim_cli/providers/fetch/pexels.py +138 -0
  22. mim_cli-0.1.0/src/mim_cli/providers/fetch/pixabay.py +137 -0
  23. mim_cli-0.1.0/src/mim_cli/providers/fetch/reddit.py +210 -0
  24. mim_cli-0.1.0/src/mim_cli/providers/fetch/unsplash.py +87 -0
  25. mim_cli-0.1.0/src/mim_cli/providers/gemini.py +147 -0
  26. mim_cli-0.1.0/src/mim_cli/providers/leonardo.py +206 -0
  27. mim_cli-0.1.0/src/mim_cli/providers/replicate.py +175 -0
  28. mim_cli-0.1.0/src/mim_cli/saver.py +230 -0
  29. mim_cli-0.1.0/src/mim_cli/search.py +96 -0
  30. mim_cli-0.1.0/src/mim_cli/store.py +331 -0
  31. mim_cli-0.1.0/tests/conftest.py +11 -0
  32. mim_cli-0.1.0/tests/test_ai.py +57 -0
  33. mim_cli-0.1.0/tests/test_cli.py +251 -0
  34. mim_cli-0.1.0/tests/test_embeddings.py +77 -0
  35. mim_cli-0.1.0/tests/test_fetch_openverse.py +111 -0
  36. mim_cli-0.1.0/tests/test_fetch_providers.py +204 -0
  37. mim_cli-0.1.0/tests/test_fetch_reddit.py +163 -0
  38. mim_cli-0.1.0/tests/test_fetch_video_fallback.py +157 -0
  39. mim_cli-0.1.0/tests/test_models.py +34 -0
  40. mim_cli-0.1.0/tests/test_net_safety.py +71 -0
  41. mim_cli-0.1.0/tests/test_saver.py +192 -0
  42. mim_cli-0.1.0/tests/test_search.py +122 -0
  43. mim_cli-0.1.0/tests/test_store.py +186 -0
  44. mim_cli-0.1.0/uv.lock +2903 -0
@@ -0,0 +1,65 @@
1
+ # mim-cli 환경변수 샘플. 이 파일을 `.env`로 복사 후 값을 채워 사용하세요.
2
+ # cp .env.sample .env
3
+
4
+ # ────────────────────────────────────────────────────────────
5
+ # 저장소 경로 (선택)
6
+ # ────────────────────────────────────────────────────────────
7
+ # 기본: ~/.mim-cli
8
+ # MIM_CLI_DIR=/path/to/storage
9
+
10
+ # ────────────────────────────────────────────────────────────
11
+ # CLI 동작 기본값 (선택)
12
+ # ────────────────────────────────────────────────────────────
13
+ # MIM_CLI_PRETTY=1 # 사람용 Rich 출력 기본 활성화 (기본은 JSON)
14
+ # MIM_CLI_TIMEOUT=120 # HTTP 타임아웃 (초)
15
+ # MIM_CLI_ASSUME_YES=1 # 확인 프롬프트 자동 승인
16
+
17
+ # ────────────────────────────────────────────────────────────
18
+ # 생성 프로바이더 (이미지 생성 API)
19
+ # ────────────────────────────────────────────────────────────
20
+
21
+ # Google Gemini — 쉼표로 여러 키 지정 가능 (429 시 자동 다음 키로 전환)
22
+ # 발급: https://aistudio.google.com/apikey
23
+ GEMINI_API_KEYS=
24
+
25
+ # Replicate — FLUX, SD 3.5, Ideogram, Imagen 등 16종 모델 지원
26
+ # 발급: https://replicate.com/account/api-tokens
27
+ # (결제 수단 등록 필수 — 무료 크레딧도 카드 등록 후 사용 가능)
28
+ REPLICATE_API_TOKEN=
29
+
30
+ # Leonardo.ai — Phoenix, FLUX, Anime XL 등 10종 모델. 무료 150 토큰/일
31
+ # 발급: https://app.leonardo.ai/settings
32
+ LEONARDO_API_KEY=
33
+
34
+ # ────────────────────────────────────────────────────────────
35
+ # 가져오기 프로바이더 (온라인 무료 미디어 API)
36
+ # ────────────────────────────────────────────────────────────
37
+
38
+ # Giphy — GIF 밈의 핵심 소스. 개발자 키 하루 100회 무료
39
+ # 발급: https://developers.giphy.com/dashboard/
40
+ GIPHY_API_KEY=
41
+
42
+ # Unsplash — 고품질 사진. 데모 앱 시간당 50회 무료
43
+ # 발급: https://unsplash.com/developers
44
+ UNSPLASH_ACCESS_KEY=
45
+
46
+ # Pexels — 사진/비디오. 월 20,000회 무료
47
+ # 발급: https://www.pexels.com/api/new/
48
+ PEXELS_API_KEY=
49
+
50
+ # Pixabay — 사진/일러스트/벡터/비디오. 시간당 100회 무료
51
+ # 발급: https://pixabay.com/api/docs/ (계정 생성 후 문서 페이지에서 키 확인)
52
+ PIXABAY_API_KEY=
53
+
54
+ # Openverse — CC 라이선스 이미지. API 키 불필요 (환경변수 없음)
55
+ # mim fetch "cute cat" -p openverse
56
+
57
+ # Reddit — 밈 밀집도 최고. 기본 무인증 동작 (분당 10회). OAuth는 선택.
58
+ # mim fetch "surprised pikachu" -p reddit --subreddit memes
59
+ # OAuth 자격증명 (선택, 분당 100회로 상승):
60
+ # 1) https://www.reddit.com/prefs/apps 에서 "create another app"
61
+ # 2) script 타입 선택 → client_id + secret 발급
62
+ # REDDIT_CLIENT_ID=
63
+ # REDDIT_CLIENT_SECRET=
64
+ # REDDIT_USER_AGENT=my-app/0.1 (by /u/yourname) # 권장: 고유한 UA
65
+ # REDDIT_DEFAULT_SUBREDDIT=memes # --subreddit 기본값
@@ -0,0 +1,42 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - '[0-9]+.[0-9]+.[0-9]+'
7
+
8
+ jobs:
9
+ build-and-publish:
10
+ name: Build and publish
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: pypi
14
+ url: https://pypi.org/p/mim-cli
15
+ permissions:
16
+ id-token: write
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: '3.11'
25
+
26
+ - name: Verify tag matches project version
27
+ run: |
28
+ TAG="${GITHUB_REF_NAME}"
29
+ PROJECT_VERSION=$(python -c "import tomllib,sys; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
30
+ if [ "$TAG" != "$PROJECT_VERSION" ]; then
31
+ echo "::error::Tag ($TAG) does not match pyproject.toml version ($PROJECT_VERSION)"
32
+ exit 1
33
+ fi
34
+
35
+ - name: Install build tooling
36
+ run: python -m pip install --upgrade build
37
+
38
+ - name: Build sdist and wheel
39
+ run: python -m build
40
+
41
+ - name: Publish to PyPI
42
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,40 @@
1
+ # 환경변수 (민감 정보)
2
+ .env
3
+ .env.local
4
+ .env.*.local
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ .venv/
13
+ venv/
14
+ env/
15
+ *.egg-info/
16
+ dist/
17
+ build/
18
+
19
+ # uv
20
+ .uv/
21
+
22
+ # pytest
23
+ .pytest_cache/
24
+ .coverage
25
+ htmlcov/
26
+
27
+ # IDE
28
+ .vscode/
29
+ .idea/
30
+ *.swp
31
+ *.swo
32
+
33
+ # macOS
34
+ .DS_Store
35
+
36
+ # 프로젝트 로컬
37
+ downloads/
38
+ scripts/
39
+ .claude/
40
+ *.pyc
@@ -0,0 +1,87 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Build & Run
6
+
7
+ ```bash
8
+ uv sync # 의존성 설치
9
+ uv run mim --help # CLI 실행
10
+ pytest tests/ # 전체 테스트
11
+ pytest tests/test_store.py # 단일 파일 테스트
12
+ pytest tests/test_store.py::test_save_and_get -v # 단일 테스트
13
+ ```
14
+
15
+ ## Architecture
16
+
17
+ 밈 미디어 관리 CLI. Typer + Rich 기반. **AI-first 설계** — 기본 출력이 **JSON** (stdout), 진행/에러는 stderr. 사람이 직접 쓸 때는 `--pretty` 플래그로 Rich UI 활성화.
18
+
19
+ **AI 사용 규칙:**
20
+ - **이미지 생성**: 전용 프로바이더 API (Gemini / Leonardo / Replicate)
21
+ - **그 외 모든 AI 작업 (이미지 분석, 메타데이터 추출 등)**: `claude --print` subprocess만 사용 (`ai.py:MetadataGenerator`)
22
+
23
+ **3가지 미디어 입수 경로 → 단일 저장 흐름:**
24
+ 1. **`add <file>`** (수동 업로드) — 로컬 파일 복사 → 해시 중복 체크 → `saver.save_media(source="upload")`
25
+ 2. **`generate <prompt>`** (AI 생성) — 프로바이더 API 호출 → `saver.save_media(source="generated", prompt=..., model=...)`
26
+ 3. **`fetch <query> -p <provider>`** (온라인 무료 API) — 검색 → 다운로드 → 중복 체크 → `saver.save_media(source="fetched", source_url=..., source_id=..., prompt=<query>, attribution=..., license=...)`
27
+
28
+ **중복 감지 (자동):**
29
+ - 1차: `(source_provider, source_id)` 인덱스 조회 — 온라인 API 재호출 시 빠른 스킵
30
+ - 2차: `content_hash` (SHA256) 조회 — 같은 바이트면 provider가 달라도 스킵
31
+ - 중복 시 기존 아이템 반환 + `{"skipped": true, "existing_id": "..."}`
32
+ - `add --force`로 해시 중복 우회 가능
33
+
34
+ **데이터 흐름:**
35
+ - **Search**: FTS5 trigram 매칭 (3자 미만은 LIKE 폴백) 또는 `--semantic` 플래그로 벡터 유사도 검색. `--source upload|generated|fetched`, `--from-provider <name>` 필터
36
+ - **Generate**: 프롬프트 → Provider API → PNG 저장 → (옵션) `saver.save_media`. `--no-save`로 DB 스킵, `--skip-metadata`로 AI 분석 스킵
37
+ - **Fetch**: 쿼리 → 프로바이더 search API → 이미지 바이트 다운로드 → `saver.save_media` (중복 스킵)
38
+
39
+ **핵심 모듈:**
40
+ - `store.py` — SQLite CRUD + FTS5 전문검색. `PRAGMA user_version` 기반 선형 마이그레이션 프레임워크 (`MIGRATIONS` 리스트). `find_by_source`, `find_by_hash`로 중복 판별. `get_many()`는 999개 변수 제한 우회를 위해 청크 처리
41
+ - `embeddings.py` — ChromaDB + BAAI/bge-m3 (lazy load). 테스트에서는 mock 처리
42
+ - `ai.py` — **유일한 non-생성 AI 경로**. `claude --print` subprocess로 이미지 분석 후 JSON 메타데이터 파싱
43
+ - `output.py` — 글로벌 `--pretty`/`--timeout`/`--yes` 상태 관리, 시크릿 마스킹 (`AIzaSy*`, `r8_*`, UUID, Bearer), 구조화 에러 분류 (`rate_limited`, `payment_required`, `unauthorized`, `not_found`, `timeout`, `request_failed`), stdout/stderr 분리
44
+ - `saver.py` — **3가지 저장 진입점의 통합 흐름**. `save_media(data, suffix, media_type, source, ...)` → `(MediaItem, is_new)`. tempfile → 해시 → 중복 체크 → AI 메타(claude --print, 부분 override 지원) → store.save + embedding upsert
45
+ - `providers/` — 두 종류 ABC:
46
+ - `ImageProvider` (생성): Gemini/Leonardo/Replicate. `generate()`, `balance()`, `list_models()`
47
+ - `FetchProvider` (온라인 API): `providers/fetch/` 서브패키지. `search(query, limit, media_type) → list[FetchedMedia]`
48
+ - `cli.py` — 두 레지스트리 분리: `GEN_PROVIDERS`, `FETCH_PROVIDERS`. `_build_gen_provider` / `_build_fetch_provider` 분기
49
+
50
+ **저장소 구조** (`~/.mim-cli/`, `MIM_CLI_DIR` 환경변수로 오버라이드):
51
+ - `memes.db` — SQLite (media_items + FTS5 가상 테이블). `PRAGMA user_version` 현재 버전 1
52
+ - `media/` — 원본/가져온/생성된 미디어 파일 (UUID 파일명, 확장자는 mime에서 추론)
53
+ - `chroma/` — ChromaDB 벡터 저장소
54
+
55
+ **MediaItem 스키마 (v1):**
56
+ - 기본: `id`, `path`, `media_type`, `name`, `description`, `tags`, `emotions`, `context`, `created_at`, `updated_at`
57
+ - 출처/계보: `source` (upload|generated|fetched), `source_provider`, `source_url`, `source_id`, `prompt`, `model`, `content_hash`, `attribution`, `license`, `license_url`, `width`, `height`
58
+ - `from_dict()`는 **unknown-field 필터** — DB에 향후 새 컬럼 생겨도 역직렬화 안 깨짐
59
+
60
+ ## 글로벌 옵션 (서브커맨드 앞에 배치)
61
+
62
+ - `--pretty` — 사람용 Rich 출력 (환경변수 `MIM_CLI_PRETTY`). 미지정 시 JSON (AI 친화 기본)
63
+ - `--timeout N` — HTTP 타임아웃 초 (환경변수 `MIM_CLI_TIMEOUT`)
64
+ - `--yes`/`-y` — 확인 프롬프트 자동 승인 (환경변수 `MIM_CLI_ASSUME_YES`)
65
+
66
+ ## 환경변수
67
+
68
+ - `MIM_CLI_DIR` — 저장소 루트 경로 (기본: `~/.mim-cli`)
69
+ - **생성 프로바이더:**
70
+ - `GEMINI_API_KEYS` — Gemini (쉼표로 여러 키 구분, 429 시 자동 전환)
71
+ - `REPLICATE_API_TOKEN` (또는 `REPLICATE_API_KEY`) — Replicate
72
+ - `LEONARDO_API_KEY` — Leonardo.ai
73
+ - **가져오기 프로바이더:**
74
+ - `GIPHY_API_KEY` — https://developers.giphy.com/dashboard/
75
+ - `UNSPLASH_ACCESS_KEY` — https://unsplash.com/developers
76
+ - `PEXELS_API_KEY` — https://www.pexels.com/api/new/
77
+ - `PIXABAY_API_KEY` — https://pixabay.com/api/docs/
78
+ - Openverse는 키 불필요
79
+ - Reddit은 기본 무인증 (분당 10회). 선택: `REDDIT_CLIENT_ID`/`REDDIT_CLIENT_SECRET` (OAuth, 분당 100회), `REDDIT_USER_AGENT` (권장), `REDDIT_DEFAULT_SUBREDDIT`
80
+
81
+ ## 테스트 패턴
82
+
83
+ - `conftest.py`의 `tmp_store_dir` fixture로 테스트별 격리된 DB/미디어 디렉토리 생성
84
+ - EmbeddingStore는 모델 로딩이 느리므로 테스트에서 mock 처리
85
+ - **Fetch 프로바이더**: `respx` + `httpx.MockTransport`로 네트워크 mock (실제 API 호출 없음)
86
+ - **MetadataGenerator** (claude --print): 테스트에서 `@patch("mim_cli.saver.MetadataGenerator")` 로 mock
87
+ - 한국어 메타데이터 (이름, 태그, 감정, 맥락)
mim_cli-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: mim-cli
3
+ Version: 0.1.0
4
+ Summary: 밈 미디어 관리 및 검색 CLI
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: chromadb>=0.5
7
+ Requires-Dist: google-genai>=1.0
8
+ Requires-Dist: httpx>=0.27
9
+ Requires-Dist: pytest>=9.0.3
10
+ Requires-Dist: python-dotenv>=1.0
11
+ Requires-Dist: respx>=0.21
12
+ Requires-Dist: rich>=13
13
+ Requires-Dist: sentence-transformers>=3.0
14
+ Requires-Dist: typer>=0.12
@@ -0,0 +1,175 @@
1
+ # mim-cli
2
+
3
+ > **AI 에이전트를 위한 밈 미디어 관리 CLI.**
4
+ > 기본 출력이 **JSON (stdout)**, 진행/에러는 stderr로 분리 — AI가 파싱하기 쉬운 형태로 설계됨. 사람이 직접 쓸 때는 `--pretty` 플래그로 Rich UI.
5
+
6
+ MCP 서버를 띄울 필요 없이, Claude Code·Cursor·Codex 같은 코딩 에이전트가 셸을 통해 바로 호출할 수 있는 밈 저장소. 한 번의 `mim fetch`로 온라인에서 밈을 가져오고, `mim search`로 의미 기반 검색까지 한 번에 된다.
7
+
8
+ ---
9
+
10
+ ## AI-first 설계 규칙
11
+
12
+ - **기본은 JSON**. 터미널 예쁨은 opt-in (`--pretty`).
13
+ - **stdout은 결과만**, 진행/경고/에러는 **stderr**. 파이프 체이닝 안전.
14
+ - **구조화된 에러 분류**: `rate_limited` / `payment_required` / `unauthorized` / `not_found` / `timeout` / `request_failed` — 에이전트가 코드로 분기 가능.
15
+ - **시크릿 자동 마스킹**: API 키, UUID, Bearer 토큰은 출력에서 가려짐.
16
+ - **중복 감지 자동**: `(provider, source_id)` + SHA256 콘텐츠 해시. 재호출 시 API 낭비 없음.
17
+ - **이미지 생성만 전용 API**, 나머지 AI 작업(이미지 분석, 메타데이터 추출)은 `claude --print` subprocess로 위임.
18
+
19
+ ---
20
+
21
+ ## 설치
22
+
23
+ ```bash
24
+ uv sync
25
+ cp .env.sample .env # API 키 채우기 (선택)
26
+ uv run mim --help
27
+ ```
28
+
29
+ Python 3.11+ 필요. `uv tool install .` 하면 `mim` 명령이 전역으로 설치된다.
30
+
31
+ ---
32
+
33
+ ## 세 가지 미디어 입수 경로
34
+
35
+ 모두 **동일한 저장 흐름** (해시 중복 체크 → AI 메타 추출 → DB + 벡터 upsert) 을 통과한다.
36
+
37
+ ### 1. `add` — 로컬 파일 업로드
38
+
39
+ ```bash
40
+ mim add ./pepe.png
41
+ mim add ./pepe.png --name "페페" --tags "frog,sad" --force
42
+ ```
43
+
44
+ ### 2. `generate` — AI로 이미지 생성
45
+
46
+ ```bash
47
+ mim generate "수학 풀다 지친 강아지" -p gemini
48
+ mim generate "surprised pikachu" -p replicate --model flux-dev --no-save
49
+ ```
50
+
51
+ 지원 프로바이더: `gemini`, `replicate`, `leonardo`.
52
+
53
+ ### 3. `fetch` — 온라인 무료 API에서 가져오기
54
+
55
+ ```bash
56
+ mim fetch "surprised pikachu" -p giphy --limit 5
57
+ mim fetch "cute cat" -p openverse # API 키 불필요
58
+ mim fetch "happy dog" -p reddit --subreddit aww
59
+ ```
60
+
61
+ 지원 프로바이더: `giphy`, `unsplash`, `pexels`, `pixabay`, `openverse`, `reddit`.
62
+
63
+ ---
64
+
65
+ ## 검색
66
+
67
+ ```bash
68
+ mim search "페페" # FTS5 trigram (키워드)
69
+ mim search "슬픈 개구리" --semantic # bge-m3 벡터 유사도
70
+ mim search "cat" --source fetched --from-provider giphy
71
+ ```
72
+
73
+ - 3자 이상은 trigram FTS5, 미만은 LIKE 폴백.
74
+ - `--semantic`은 ChromaDB + `BAAI/bge-m3` 임베딩 (최초 1회 모델 다운로드).
75
+
76
+ ---
77
+
78
+ ## 글로벌 옵션
79
+
80
+ | 플래그 | 환경변수 | 설명 |
81
+ |---|---|---|
82
+ | `--pretty` | `MIM_CLI_PRETTY` | 사람용 Rich 출력 활성화 |
83
+ | `--timeout N` | `MIM_CLI_TIMEOUT` | HTTP 타임아웃 (초, 기본 120) |
84
+ | `--yes` / `-y` | `MIM_CLI_ASSUME_YES` | 확인 프롬프트 자동 승인 |
85
+ | — | `MIM_CLI_DIR` | 저장소 루트 (기본 `~/.mim-cli`) |
86
+
87
+ 글로벌 옵션은 **서브커맨드 앞에** 둔다: `mim --pretty search "pepe"`.
88
+
89
+ ---
90
+
91
+ ## 전체 커맨드
92
+
93
+ | 커맨드 | 설명 |
94
+ |---|---|
95
+ | `add <file>` | 로컬 파일 업로드 + AI 메타 자동 생성 |
96
+ | `generate <prompt>` | AI 이미지 생성 (자동 저장, `--no-save`로 스킵) |
97
+ | `fetch <query> -p <provider>` | 온라인 API에서 검색·다운로드·저장 |
98
+ | `search <query>` | 키워드/시맨틱 검색 |
99
+ | `list` | 전체 목록 |
100
+ | `get <id>` | 상세 조회 |
101
+ | `edit <id>` | 메타데이터 수정 (AI 재분석도 가능) |
102
+ | `remove <id>` | 삭제 |
103
+ | `balance -p <provider>` | 생성 프로바이더 크레딧 조회 |
104
+ | `providers` | 등록된 프로바이더 + 인증 상태 |
105
+ | `models -p <provider>` | 지원 모델 별명 |
106
+ | `info` | 저장소 경로·상태 덤프 |
107
+
108
+ ---
109
+
110
+ ## 환경변수
111
+
112
+ ### 생성 프로바이더
113
+
114
+ | 키 | 용도 |
115
+ |---|---|
116
+ | `GEMINI_API_KEYS` | Gemini (쉼표 여러 키, 429 시 자동 전환) |
117
+ | `REPLICATE_API_TOKEN` | Replicate (FLUX / SD / Imagen 등) |
118
+ | `LEONARDO_API_KEY` | Leonardo.ai (Phoenix / FLUX 등) |
119
+
120
+ ### 가져오기 프로바이더
121
+
122
+ | 키 | 용도 |
123
+ |---|---|
124
+ | `GIPHY_API_KEY` | Giphy (GIF 밈 핵심 소스) |
125
+ | `UNSPLASH_ACCESS_KEY` | Unsplash (고품질 사진) |
126
+ | `PEXELS_API_KEY` | Pexels (사진/비디오) |
127
+ | `PIXABAY_API_KEY` | Pixabay (사진/일러스트/벡터/비디오) |
128
+ | — | Openverse는 키 불필요 |
129
+ | `REDDIT_CLIENT_ID` / `REDDIT_CLIENT_SECRET` | Reddit OAuth (선택, 분당 100회). 없으면 무인증 10회/분 |
130
+ | `REDDIT_USER_AGENT` / `REDDIT_DEFAULT_SUBREDDIT` | Reddit 추가 설정 (선택) |
131
+
132
+ `.env` 파일은 자동 로드된다 (python-dotenv).
133
+
134
+ ---
135
+
136
+ ## 저장소 구조
137
+
138
+ ```
139
+ ~/.mim-cli/
140
+ ├── memes.db # SQLite + FTS5 (PRAGMA user_version으로 마이그레이션)
141
+ ├── media/ # 원본 바이너리 (UUID 파일명)
142
+ └── chroma/ # 벡터 임베딩 (bge-m3)
143
+ ```
144
+
145
+ `MIM_CLI_DIR`로 경로 오버라이드 가능.
146
+
147
+ ---
148
+
149
+ ## 아키텍처 한눈에
150
+
151
+ ```
152
+ ┌─ add ────┐
153
+ ├─ generate┼─► saver.save_media() ──► hash dedup ──► AI 메타 ──► store.save + embedding upsert
154
+ └─ fetch ──┘ (claude --print)
155
+ ```
156
+
157
+ - `store.py` — SQLite + FTS5, `PRAGMA user_version` 기반 선형 마이그레이션
158
+ - `embeddings.py` — ChromaDB + bge-m3 (lazy load)
159
+ - `saver.py` — 3가지 입수 경로의 단일 통합 진입점
160
+ - `ai.py` — 비-생성 AI의 유일한 경로 (`claude --print` subprocess)
161
+ - `output.py` — `--pretty` 라우팅, 시크릿 마스킹, 에러 분류
162
+ - `providers/` — 생성 (`ImageProvider`) / 가져오기 (`FetchProvider`) ABC 두 종
163
+
164
+ ---
165
+
166
+ ## 개발
167
+
168
+ ```bash
169
+ pytest tests/ # 전체 테스트
170
+ pytest tests/test_store.py::test_save_and_get -v # 단일 테스트
171
+ ```
172
+
173
+ 테스트는 `conftest.py:tmp_store_dir` fixture로 DB/미디어 디렉토리 격리. EmbeddingStore와 `claude --print`는 mock 처리. Fetch 프로바이더는 `respx` + `httpx.MockTransport`로 네트워크 없이 검증.
174
+
175
+ 자세한 내부 규칙과 컨벤션은 [`CLAUDE.md`](./CLAUDE.md) 참고.