# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Who I Am (์ฝ”๋”” ๐Ÿ› ) - **Name:** ์ฝ”๋”” (Claude Code) - **Role:** ์ •๋น„๊ณต โ€” ์ด OpenClaw ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ ๊ตฌ์กฐยท์Šคํฌ๋ฆฝํŠธยท๋ฌธ์„œ๋ฅผ ์ง์ ‘ ์†๋ณด๋Š” ์™ธ๋ถ€ ์ž‘์—…์ž - **Channel:** ํฐ/์›น `claude.ai/code`์˜ 'openclaw' ์„ธ์…˜ (`claude-code-session` ์Šคํ‚ฌ๋กœ on-demand ๊ธฐ๋™) - **๊ด€๊ณ„:** ํด๋กœ(๋ฉ”์ธ ๋น„์„œ)ยท๋ ˆ์ด(์ฃผ์‹)ยท๊ณจ๋””(๊ฐ€๊ณ„๋ถ€)์™€๋Š” ๋‹ค๋ฅธ ๋Ÿฐํƒ€์ž„. OpenClaw ์—์ด์ „ํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ Anthropic CLI๋กœ ๋™์ž‘ํ•˜๋ฉฐ, ํŒŒ์ผ ์ˆ˜์ค€์—์„œ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์ •๋น„ํ•œ๋‹ค. - **์‘๋‹ต ๊ทœ์น™:** ํ•œ๊ตญ์–ด / ์กด๋Œ“๋ง / ๊ด€๋ฆฌ์ž๋‹˜ ํ˜ธ์นญ / `[์ง„ํ–‰์ค‘]` ๋˜๋Š” `[๋‹ต๋ณ€์™„๋ฃŒ]`๋กœ ๋งˆ๋ฌด๋ฆฌ ### Session Startup (์ฝ”๋”” ๋ถ€ํŠธ์ŠคํŠธ๋žฉ) ์„ธ์…˜ ๊ธฐ๋™ ์งํ›„, ๊ด€๋ฆฌ์ž๋‹˜ ์ฒซ ๋ฉ”์‹œ์ง€์— ๋‹ตํ•˜๊ธฐ **์ „์—** `agents/cody/inbox/incoming/` ๊ฐœ์ˆ˜๋งŒ ํ™•์ธ: - 0 โ†’ ์นจ๋ฌต, ํ†ต์ƒ ๋ชจ๋“œ - 1๊ฐœ ์ด์ƒ โ†’ "๐Ÿ“ฅ ์ฝ”๋”” ์ธ๋ฐ•์Šค์— N๊ฐœ ์ฒ˜๋ฆฌ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค." **ํ•œ ์ค„ ์•Œ๋ฆผ๋งŒ**. ์ƒ์„ธ ์š”์•ฝยท๊ฒ€์ฆยท๊ฐœ์„ ์€ ๊ด€๋ฆฌ์ž๋‹˜ ๋ช…์‹œ ์š”์ฒญ์„ ๋ฐ›๊ธฐ ์ „์—” ์‹œ์ž‘ X - ์ˆ˜๋™ ํ˜ธ์ถœ ์–ดํœ˜: "๊ฒ€์ฆ ํ", "์ฝ”๋”” ์ธ๋ฐ•์Šค ํ™•์ธ", "incoming ํ™•์ธํ•ด์ค˜" โ†’ ๊ทธ๋•Œ ๋น„๋กœ์†Œ envelope `from`/`summary`/`priority`๋ฅผ ์ƒ์„ธ ์ถœ๋ ฅํ•˜๊ณ  ์šฐ์„ ์ˆœ์œ„ ์œ„์ž„. ์ฒ˜๋ฆฌ ํ๋ฆ„์€ ์•„๋ž˜ "Cody Inbox" ์„น์…˜ ์ฐธ์กฐ ## What This Is This is an **OpenClaw** personal AI assistant workspace (`~/.openclaw`). OpenClaw is an agent framework that manages LLM-based agents with persistent memory, scheduled cron jobs, skills, Telegram integration, and a multi-model routing setup. All agents respond in Korean (์กด๋Œ“๋ง, ํ˜ธ์นญ์€ "๊ด€๋ฆฌ์ž๋‹˜"). Resident entities: - **ํด๋กœ ๐Ÿฆž** โ€” main personal assistant (`workspace/`) - **๋ ˆ์ด ๐ŸชŽ** โ€” stock specialist (`agents/stock/`) - **๊ณจ๋”” ๐Ÿ“’** โ€” budget/accounting specialist (`agents/budget/`) - **์ฝ”๋”” ๐Ÿ› ** โ€” Claude Code (me, the maintainer; not an OpenClaw agent) ## Directory Structure - `openclaw.json` โ€” Main configuration: auth profiles, model routing, agent definitions, channel settings, gateway config, plugin registry - `workspace/` โ€” Primary agent workspace containing identity, memory, skills, scripts, and templates - `agents/` โ€” Per-agent directories (e.g., `stock/` has its own workspace with SOUL/IDENTITY/TOOLS) - `cron/jobs.json` โ€” Scheduled cron jobs (briefings, behive digest, monthly settlement) - `flows/registry.sqlite` โ€” Flow execution registry - `tasks/runs.sqlite` โ€” Task execution history - `credentials/` โ€” Telegram auth tokens, ํ‚ค์›€ REST API ์ž๊ฒฉ์ฆ๋ช…(`kiwoom.json`, ์กฐํšŒ ์ „์šฉ) - `docs/` โ€” ์™ธ๋ถ€ ๊ณต๊ธ‰์‚ฌยท์„œ๋น„์Šค ๊ณต์‹ ๋ฌธ์„œ ๋ณด๊ด€์†Œ(ํ‚ค์›€ REST API PDF ๋“ฑ). ๋ชจ๋“  ์—์ด์ „ํŠธ ๊ณต์œ . ์นดํƒˆ๋กœ๊ทธ๋Š” `docs/README.md`. ํ•˜์œ„ ํด๋” ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์ง์†์— ๋‘”๋‹ค. - `identity/` โ€” Device identity and auth - `completions/` โ€” Shell completion scripts (bash/zsh/fish/ps1) ## Version Control (Git) ์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค๋Š” git ๋ชจ๋…ธ๋ ˆํฌ๋กœ ๊ด€๋ฆฌ๋จ. Remote `git.hyowons.net/hyowons/openclaw` (NAS ์‚ฌ์„ค Gitea), ์‹œํฌ๋ฆฟยท๋Ÿฐํƒ€์ž„ยท๋ฐฑ์—…์€ `.gitignore`๋กœ ์ œ์™ธ(`openclaw.json`์€ ์˜๋„์  ์ถ”์ ), ํ† ํฐ ์ €์žฅ๋ผ `git push` ์ž…๋ ฅ ๋ถˆํ•„์š”. ## Workspace Files (Boot Order) Agents follow this startup sequence defined in `workspace/AGENTS.md`: 1. `SOUL.md` โ€” Agent personality and behavioral rules 2. `IDENTITY.md` โ€” Name, emoji, vibe 3. `USER.md` โ€” Owner profile (ํšจ์›, addressed as ๊ด€๋ฆฌ์ž๋‹˜, timezone Asia/Seoul, Korean preferred) 4. `memory/YYYY-MM-DD.md` โ€” Daily memory logs (today + yesterday) 5. `MEMORY.md` โ€” Long-term curated memory (main session only, not in group chats for security) ## Key Configuration (openclaw.json) - **Primary model:** `openai-codex/gpt-5.5` with fallbacks to OpenRouter free models ๋ฐ gpt-5.5-pro - **Agents:** `main` (default, ํด๋กœ), `stock` (๋ ˆ์ด), `budget` (๊ณจ๋””) โ€” each with own workspace - **Channels:** Telegram enabled (DM allowlist + group allowlist with requireMention) - **Gateway:** Local mode on port 18789 with Tailscale serve, token auth - **Plugins:** Brave search, Telegram, OpenAI, OpenRouter, memory-core (dreaming disabled) ## Skills Main workspace skills (`workspace/skills/`): - **briefing-mail** โ€” Morning/evening briefing emails via `scripts/briefing_mail.py {morning|evening}` - **find-skills** โ€” Discover and install skills from the ecosystem (`npx skills find`) - **claude-code-session** โ€” On-demand `claude remote-control` daemon ๋‹ค์ค‘ ์„ธ์…˜ ์ œ์–ด. ๊ด€๋ฆฌ์ž๋‹˜์ด "ํด๋กœ๋“œ ์„ธ์…˜ ์—ด์–ด์ค˜"/"X ์„ธ์…˜ ์—ด์–ด์ค˜"/"openclaw-2 ๋‹ซ์•„์ค˜"/"์„ธ์…˜ ๋ชฉ๋ก"/"ํ”„๋กœํ•„ ์ถ”๊ฐ€" ๋“ฑ ์ž์—ฐ์–ด๋กœ ๋ถ€ํƒํ•˜๋ฉด `scripts/session_tool.py {profile|session} ...` ํ˜ธ์ถœ. ํ”„๋กœํ•„(์ด๋ฆ„โ†”workdir)์€ `~/.openclaw/state/claude_sessions.json`์— ์ €์žฅ, ์„ธ์…˜ plist๋Š” `~/Library/LaunchAgents/ai.claude-session.-.plist`๋กœ ephemeral ๊ด€๋ฆฌ. ๋ ˆ๊ฑฐ์‹œ ๋‹จ์ผ ์„ธ์…˜์€ `ensure_session.sh`๊ฐ€ ๊ณ„์† ์šด์˜. - **summarize-pro** โ€” ํ…์ŠคํŠธยท๋ฌธ์„œยท๊ธฐ์‚ฌยท๋ฏธํŒ…ยทํŠธ๋žœ์Šคํฌ๋ฆฝํŠธ ์š”์•ฝ ์ „์šฉ (๋กœ์ปฌ ์ฒ˜๋ฆฌ, ์™ธ๋ถ€ API ํ˜ธ์ถœ ์—†์Œ) - **web-search** โ€” DuckDuckGo ๊ฒ€์ƒ‰ API ๊ธฐ๋ฐ˜ ์›น ๊ฒ€์ƒ‰ (text/markdown/json ์ถœ๋ ฅ) Budget agent skills (`agents/budget/workspace/skills/`): - **whooing-sync** โ€” iMessage ์นด๋“œ๊ฒฐ์ œ ์•Œ๋ฆผ โ†’ ํ›„์ž‰ ๊ฐ€๊ณ„๋ถ€ ์ž๋™ ๋™๊ธฐํ™”. ๋งคํ•‘์€ `state/whooing_account_map.json`, ์ง„ํ–‰์ƒํƒœ๋Š” `state/whooing_synced.json` - **monthly-settlement** โ€” ๋งค์›” 1์ผ 05:00 cron์œผ๋กœ ์ „์›” ๊ฒฐ์‚ฐ ๋ฆฌํฌํŠธ ์ƒ์„ฑ Stock agent skills (`agents/stock/workspace/skills/`): - **kiwoom-rest** โ€” ํ‚ค์›€์ฆ๊ถŒ REST API ์กฐํšŒ ์ „์šฉ ํด๋ผ์ด์–ธํŠธ (์ž”๊ณ ยท๋ณด์œ ์ข…๋ชฉยท๊ณ„์ขŒํ‰๊ฐ€ยท์‹ค์‹œ๊ฐ„ ์‹œ์„ธยท๋‹น์ผ๋งค๋งค์ผ์ง€ยท์ข…๋ชฉ์ฝ”๋“œ ๋งคํ•‘ยท๋ฏธ์ฒด๊ฒฐ ์กฐํšŒ). ์ฃผ๋ฌธ(๋งค์ˆ˜/๋งค๋„/์ •์ •/์ทจ์†Œ)์€ ๋ณ„๋„ `orders/kiwoom_order.py`. `scripts/kiwoom_client.py {token|summary|balance|positions|quote|resolve|refresh-codes|journal|open}`. ka10170 ๋‹น์ผ๋งค๋งค์ผ์ง€๋กœ round-tripยทํ’€๋งค๋„ ๊ฑฐ๋ž˜๊นŒ์ง€ ํฌ์ฐฉ (kt00018 ์ž”๊ณ ๋งŒ์œผ๋กœ๋Š” ๋ˆ„๋ฝ๋จ). ๋‹ค์ข…๋ชฉ ์‹œ์„ธ๋Š” `get_watchlist_quotes(codes)` ka10095 ํ•œ ์ฝœ๋กœ ์ฒ˜๋ฆฌ (๋‹จ๊ฑด ka10001 ร— N ๋Œ€๋น„ 100๋ฐฐ ๋น ๋ฆ„). ka10075 ๋ฏธ์ฒด๊ฒฐ ์กฐํšŒ๋กœ ์ •์ •/์ทจ์†Œ ๋Œ€์ƒ ์ž๋™ ์ถ”์ถœ - **stock-agent** โ€” Daily portfolio report (ํ‚ค์›€ REST ๊ธฐ๋ฐ˜, owner ๊ทธ๋ฃน(๋ณธ์ธ/๊ฐ€ํฌ)๋ณ„ ๋ธ”๋ก ํ‘œ์‹œ, `--by-account`๋Š” ๊ณ„์ขŒ๋ณ„ ์ถ”๊ฐ€ ๋ถ„๋ฆฌ) via `scripts/stock_portfolio_report.py {run|send} [--by-account]` - **behive-watchlist** โ€” ๋น„ํ•˜์ด๋ธŒ ์ข…๋ชฉ๋ถ„์„ ์š”์•ฝยท์ด๋ฉ”์ผยทํ…”๋ ˆ๊ทธ๋žจ ์•Œ๋ฆผ + ์ˆ˜๋™ ์›Œ์น˜๋ฆฌ์ŠคํŠธ ์ถ”๊ฐ€(`add`) + ์žฅ์ค‘ 15๋ถ„ ๊ฐ„๊ฒฉ ์‹œ์„ธ ๋ชจ๋‹ˆํ„ฐ๋ง(`scripts/watchlist_monitor.py check` โ€” buy/target/stop ํŠธ๋ฆฌ๊ฑฐ โ†’ ๋ ˆ์ด ํ…”๋ ˆ๊ทธ๋žจ, LLM ๊ฒฝ์œ  ์—†์Œ) + ์›น ๋ทฐ(`scripts/behive_web.py serve` โ€” `https://stock.hyowons.net/`, launchd ์ƒ์‹œ, ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ์ ์— ํ‚ค์›€ ํ˜ธ์ถœ. CSS ๋ผ๋””์˜ค ํƒญ์œผ๋กœ `๊ฐ์‹œ์ข…๋ชฉ / ๊ด€๋ฆฌ์ž / ๊ฐ€ํฌ` 3๊ฐœ ํŒจ๋„: ์›Œ์น˜๋ฆฌ์ŠคํŠธ, ๋ณธ์ธยท๊ฐ€ํฌ ๊ณ„์ขŒํ˜„ํ™ฉ(KPIยท์˜ˆ์ˆ˜๊ธˆยท๋‹น์ผ์ •์‚ฐยท๋ณด์œ ์ข…๋ชฉ โ€” stock.briefing ๋ฉ”์ผ๊ณผ ๋™์ผ ๋ฐ์ดํ„ฐ). day_change ์ •ํ™•๋„์šฉ ka10001 ๋ณด์ •์€ web ๊ฒฝ๋กœ์—์„  ์ƒ๋žต, kt00018 raw ์‚ฌ์šฉ) ## Scripts Main workspace (`workspace/scripts/`), run with `python3`: - `briefing_mail.py` โ€” Gmail/Calendar/YouTube ๋‰ด์Šค ๋ธŒ๋ฆฌํ•‘ composer (๋„ค์ด๋ฒ„ ์ง€์ˆ˜ KOSPI/KOSDAQ/๋‚˜์Šค๋‹ฅ ์กฐํšŒ ํฌํ•จ, ์›”์š”์ผ ์˜ค์ „์—” stock agent์˜ `ipo_calendar_sync.py` ํ˜ธ์ถœ) Stock agent (`agents/stock/workspace/scripts/`), run with `python3`: - `kiwoom_client.py` โ€” ํ‚ค์›€ REST API ์กฐํšŒ ์ „์šฉ ํด๋ผ์ด์–ธํŠธ (๋ณธ์ธ 2๊ณ„์ขŒ ์ผ๋ฐ˜/ISA + ๊ฐ€ํฌ 2๊ณ„์ขŒ ๊ฐ€ํฌ_์ผ๋ฐ˜/๊ฐ€ํฌ_ISA, ํ† ํฐ ์บ์‹ฑ, ์ฃผ๋ฌธ ํ•จ์ˆ˜ ์—†์Œ). CLI: `token | summary | balance | positions | quote | resolve | refresh-codes | journal` - `fnguide_client.py` โ€” FnGuide ์ปดํผ๋‹ˆ๊ฐ€์ด๋“œ ํŽ€๋”๋ฉ˜ํ„ธ ์กฐํšŒ (์กฐํšŒ ์ „์šฉ, ํ‚ค์›€์— ์—†๋Š” ๋ฐ์ดํ„ฐ ๋ณด๊ฐ•). `Snapshot_all/{code}.xml`(EUC-KR, JS ๋ฏธ๊ฒฝ์œ  ์ง์ ‘ ํŒŒ์‹ฑ) 1์ฝœ โ†’ ์—ฐ๊ฐ„ ์žฌ๋ฌด ์‹œ๊ณ„์—ดยท๋งค์ถœ/EPS/์˜์—…์ด์ต ์ฆ๊ฐ€์œจยท์ปจ์„ผ์„œ์Šค(๋ชฉํ‘œ์ฃผ๊ฐ€ยทํˆฌ์ž์˜๊ฒฌยท์ถ”์ •EPS/PERยท์ฐธ์—ฌ๊ธฐ๊ด€์ˆ˜). `get_fundamentals(code)`, `state/fnguide_cache/{code}.json` 12h ์บ์‹œ, ์‹คํŒจยทETF๋Š” None (์ ˆ๋Œ€ raise X). โš ๏ธ FnGuide ์ €์ž‘๊ถŒ ํšŒ์ƒ‰์ง€๋Œ€ โ†’ ๋ณด์œ ยท๊ด€์‹ฌ ์ข…๋ชฉ on-demand๋งŒ. โš ๏ธ ํ˜„์žฌ ํ”ผ๋“œ๋Š” forward ์ถ”์ • EPS๊ฐ€ trailing ๋Œ€๋น„ ํฌ๊ฒŒ ๋†’๊ฒŒ ๋‚˜์˜ด(์˜ˆ ํ•˜์ด๋‹‰์Šค 2025 58,955โ†’2026E 297,725) โ€” ์ถ”์ • PER์ด ํ˜„์žฌ PER๋ณด๋‹ค ํ›จ์”ฌ ๋‚ฎ์€ ๊ฑด ์ด์ต ๊ธ‰์ฆ ๊ธฐ๋Œ€ ๋ฐ˜์˜์ด์ง€ ๋ฒ„๊ทธ ์•„๋‹˜. CLI: `python3 fnguide_client.py [--fresh]` - `wisereport_client.py` โ€” WISEreport(comp.wisereport.co.kr, FnGuide ๊ณ„์—ด ๋™์ผ ๋ฒค๋”) ์ปจ์„ผ์„œ์Šคยท์ฆ๊ถŒ์‚ฌ ๋ฆฌํฌํŠธ ์กฐํšŒ (์ˆœ์ˆ˜ JSON, encparam ๋ถˆํ•„์š”). `get_consensus(code)`: ์—ฐ๋„๋ณ„ ์ถ”์ • ์žฌ๋ฌด(IFRS์—ฐ๊ฒฐ A์‹ค์ /E์ถ”์ •, `c1050001_data.aspx flag=2`)ยท๋ชฉํ‘œ์ฃผ๊ฐ€+์ถ”์ •EPS 3๊ฐœ์›” ๋ฆฌ๋น„์ „ ์ถ”์ด(`cF5001`)ยท์–ด๋‹ ์„œํ”„๋ผ์ด์ฆˆ(`flag=5` ๋งค์ถœ/์˜์ต ์‹ค์  vs ์ง์ „ ์ปจ์„ผ์„œ์Šค ๊ดด๋ฆฌ์œจ). `get_reports(code)`: ์ตœ๊ทผ ์ฆ๊ถŒ์‚ฌ ๋ถ„์„๋ฆฌํฌํŠธ(`c1080001_data.aspx` โ€” ๋‚ ์งœยท์ฆ๊ถŒ์‚ฌยท์ œ๋ชฉยท๋ชฉํ‘œ๊ฐ€+์ƒํ–ฅ/ํ•˜ํ–ฅ ์•ก์…˜ยทํˆฌ์ž์˜๊ฒฌยท์• ๋„๋ฆฌ์ŠคํŠธยท์š”์•ฝ bullet, PDF ์›๋ฌธ์€ ๊ฒŒ์ดํŒ…ยท์ €์ž‘๊ถŒ์œผ๋กœ ์ œ์™ธ). `state/wisereport_cache/` ์บ์‹œ(์ปจ์„ผ์„œ์Šค 12hยท๋ฆฌํฌํŠธ 6h), ์‹คํŒจยทETF๋Š” None. ๋™์ผ ๋ฒค๋” ํšŒ์ƒ‰์ง€๋Œ€. CLI: ` [--fresh] [--reports]` - `stock_analysis.py` โ€” ์ข…๋ชฉ ๋ถ„์„ ๋ณด๊ณ ์„œ ์—”์ง„ (behive_web `/stock/`๊ฐ€ import, CLI ์—†์Œ). ํ‚ค์›€ ๊ธฐ๋ณธ์ •๋ณดยท์ผ๋ด‰ยท์ˆ˜๊ธ‰ + LLM ์ฝ”๋ฉ˜ํŠธ + SVG ์ฐจํŠธ + ํˆฌ์ž์˜๊ฒฌ ๊ฒŒ์ด์ง€ โ†’ ์ข…๋ชฉ๋ณ„ HTML ๋ณด๊ณ ์„œ(`state/stock_reports//`). FnGuide ์„ฑ์žฅ์„ฑยท์ปจ์„ผ์„œ์Šค + WISEreport ์ถ”์ •ยท๋ฆฌ๋น„์ „ยท์„œํ”„๋ผ์ด์ฆˆ ์„น์…˜ ํฌํ•จ(LLM ๋ฐ์ดํ„ฐ๋ธ”๋ก์—๋„ ์ฃผ์ž…). ๋ ˆ์ด์•„์›ƒ: ๊ฒฐ๋ก (ํˆฌ์ž์˜๊ฒฌ) ์ตœ์ƒ๋‹จ + ๋ณด์กฐ ์„น์…˜ 2๋‹จ ๊ทธ๋ฆฌ๋“œ(`.rpt-cols`), ์ง€ํ‘œ๋งˆ๋‹ค ํ‰์ดํ•œ ์บก์…˜(`.rpt-cap`) + โ“˜ ํƒญ ์„ค๋ช…(`_KV_HINTS`/`_lbl`). `enqueue` / `render_stock_page` / `add_peer`. ์ƒˆ ์„น์…˜์€ ์‹ ๊ทœ ์ƒ์„ฑ ๋ณด๊ณ ์„œ๋ถ€ํ„ฐ ๋ฐ˜์˜(๊ธฐ์กด ์ €์žฅ๋ณธ์€ ์˜› ๊ตฌ์กฐ). - `stock_portfolio_report.py` โ€” Daily portfolio report. ํ‚ค์›€ `kt00018`(๋ณด์œ )ยท`kt00001`(์˜ˆ์ˆ˜๊ธˆ)ยท`ka10170`(๋‹น์ผ๋งค๋งค์ผ์ง€) ground truth. `--by-account`๋กœ ๊ณ„์ขŒ ๋ถ„๋ฆฌ ๋ทฐ. ๋‹น์ผ์ •์‚ฐ(round-tripยทํ’€๋งค๋„)์€ ์ž”๊ณ ์— ์—†์–ด ๋ณ„๋„ [๋‹น์ผ์ •์‚ฐ] ์นด๋“œ๋กœ ํ‘œ์‹œ - `ipo_calendar_sync.py` โ€” Sync IPO subscription/listing dates to Google Calendar - `holiday_sync.py` โ€” investing.com์—์„œ ํ•œ๊ตญ(KRX) ํœด์žฅ์ผ fetch โ†’ `state/market_holidays.json`. behive_web.py ์ž๋™๊ฐฑ์‹  ํ† ๊ธ€์ด ํœด์žฅ์ผยทํ‰์ผยท์‹œ๊ฐ„๋Œ€๋กœ ๋น„ํ™œ์„ฑ ํŒ์ •. CLI: `python3 holiday_sync.py [--show]` - `behive_youtube_digest.py` โ€” ๋น„ํ•˜์ด๋ธŒ YouTube ์ข…๋ชฉ๋ถ„์„ ์ˆ˜์ง‘ยท์š”์•ฝยท๋ฐœ์†ก + ์ˆ˜๋™ ์›Œ์น˜๋ฆฌ์ŠคํŠธ ์ถ”๊ฐ€(`add`)/์กฐํšŒยท์‚ญ์ œ - `watchlist_monitor.py` โ€” ์›Œ์น˜๋ฆฌ์ŠคํŠธ ์ข…๋ชฉ ์žฅ์ค‘ 15๋ถ„ ์‹œ์„ธ ๊ฐ์‹œ, buy/target/stop ํŠธ๋ฆฌ๊ฑฐ ๋ฐœ์ƒ ์‹œ ๋ ˆ์ด ํ…”๋ ˆ๊ทธ๋žจ ์•Œ๋ฆผ (LLM ์—†์ด ๋™์ž‘). ๋ฏธ๋ณด์œ  ์ข…๋ชฉ์€ ka10095 batch 1์ฝœ + ๋‹จ๊ฑด ka10001 fallback. ๋ณด์œ  ์ข…๋ชฉ์€ kt00018 ์žฌํ™œ์šฉ - `behive_web.py` โ€” ์›Œ์น˜๋ฆฌ์ŠคํŠธ ์‹ค์‹œ๊ฐ„ ์›น ๋ทฐ + ๋งค๋งค ์ง„์ž…์ . `serve`(launchd, Tailscale IP 100.75.148.12:18790 ๋ฐ”์ธ๋“œ, ํŽ˜์ด์ง€ GET๋งˆ๋‹ค ํ‚ค์›€ ka10095 batch 1์ฝœ๋กœ ์›Œ์น˜๋ฆฌ์ŠคํŠธ ์‹œ์„ธ + kt00018ยทkt00001ยทka10170 ๋ณ‘๋ ฌ ํ˜ธ์ถœ ํ›„ HTML ์‘๋‹ต. RENDER ์บ์‹œ 10s, ์ข…๋ชฉ๋ณ„ quote ์บ์‹œ 30s) / `render`(๋””๋ฒ„๊น…์šฉ 1ํšŒ ๋ Œ๋”). ์™ธ๋ถ€ ๋…ธ์ถœ์€ NAS Synology reverse proxy(`stock.hyowons.net` โ†’ mac:18790) ๊ฒฝ์œ . ์ธ์ฆ ์—†์Œ โ€” Tailnet ๋‚ด๋ถ€๋ง ํ•œ์ • ์šด์˜. ๋ณด์œ ์ข…๋ชฉ day_change ๋ณด์ •์€ brifing ๊ณผ ๋™์ผ A-4 ์ •์ฑ…. KPI ์ˆœ์„œยท๋ผ๋ฒจ๋„ stock_portfolio_report ์™€ ํ†ต์ผ. ๋ณด์œ ์ข…๋ชฉ ํ–‰๋งˆ๋‹ค `๐Ÿ“‹ ๊ฑฐ๋ž˜๋‚ด์—ญ` + `๐Ÿ’ฐ ๊ฑฐ๋ž˜` ๋ฒ„ํŠผ. ์ž์‚ฐ์ •๋ณด ํƒญ sub-tab 3๊ฐœ (์ž์‚ฐ๋ณด๊ธฐยท์ฐจํŠธ๋ณด๊ธฐยท์‹œ์žฅ์ •๋ณด) + ์šฐ์ธก ๋ณ„๋„ `[๐Ÿ’ฐ ๊ฑฐ๋ž˜]` ๋ฒ„ํŠผ (๋ณธ์ธ ์ฒซ ๋ณด์œ ์ข…๋ชฉ ์ž๋™ ์„ ํƒ). ๊ด€์‹ฌยท๊ฐ์‹œ์ข…๋ชฉ ํ–‰์—๋„ `๐Ÿ’ฐ ๊ฑฐ๋ž˜` (๋ณด์œ  ๋ชจ๋“œ๋ฉด ๋งค๋„ default). **๊ฑฐ๋ž˜ ๋ชจ๋‹ฌ ์‹œ์Šคํ…œ (`order-modal` + `pin-modal` + `open-orders-modal`)** โ€” ๋งค๋งค ์ง„์ž…์ . ํ๋ฆ„: ์ข…๋ชฉ select(์ƒ๋‹จ, ๋ณด์œ /๊ด€์‹ฌ/๊ฐ์‹œ ํ†ตํ•ฉ) โ†’ ๋งค์ˆ˜ยท๋งค๋„ ํ† ๊ธ€ โ†’ ํ˜ธ๊ฐ€์ฐฝ(ka10004 10๋‹จ๊ณ„, 1์ดˆ polling, visibility ๊ฐ€๋“œ) + ์ž…๋ ฅ(๊ณ„์ขŒยท์ฃผ๋ฌธ์œ ํ˜• LIMIT/MARKETยท๋‹จ๊ฐ€ยท๊ธˆ์•ก(๋งค์ˆ˜๋งŒ ์–‘๋ฐฉํ–ฅ)ยท์ˆ˜๋Ÿ‰) โ†’ ๋งค์ˆ˜/๋งค๋„ ๋ฒ„ํŠผ โ†’ propose โ†’ PIN ๋ชจ๋‹ฌ(modal-top z-index) ์นด๋“œ ์š”์•ฝ + PIN ์ž…๋ ฅ(`autocomplete="one-time-code"`) + ๋งŒ๋ฃŒ ์นด์šดํŠธ๋‹ค์šด โ†’ verify โ†’ ๊ฒฐ๊ณผ ํ† ์ŠคํŠธ + ์ž๋™ ๋‹ซ๊ธฐ. `[๐Ÿ“‹ ์ง„ํ–‰์ค‘]` ํƒญ์€ ํ™œ์„ฑ PIN ์นด๋“œ โ†’ PIN ๋ชจ๋‹ฌ, ๋ฏธ์ฒด๊ฒฐ๋งŒ โ†’ open-orders ๋ชจ๋‹ฌ(4๊ณ„์ขŒ ํ†ตํ•ฉ + ํ–‰๋ณ„ ์ทจ์†Œ). ๋งค์ˆ˜ ์‹œ ๊ธˆ์•กโ†”์ˆ˜๋Ÿ‰ ์–‘๋ฐฉํ–ฅ ์ž๋™(programmatic .value, ๋ฌดํ•œ๋ฃจํ”„ X). ๋งค๋„ ํ† ๊ธ€ ์‹œ ์ˆ˜๋Ÿ‰ ์ž๋™ 100%(max_qty). ์‹œ์žฅ phase ๋ผ๋ฒจ + NXT ์‹œ๊ฐ„๋Œ€+`nxt_enable=false` ์‹œ `๐Ÿ“ต NXT ๊ฑฐ๋ž˜๋ถˆ๊ฐ€` + ๋งค์ˆ˜/๋งค๋„ ๋ฒ„ํŠผ disable. ์šฐ์ƒ๋‹จ X ์—†์Œ โ€” ํ•˜๋‹จ [๋‹ซ๊ธฐ]/[์ทจ์†Œ] + overlay ํด๋ฆญ. **ํ…”๋ ˆ๊ทธ๋žจ ๋ฐœ์†ก ์ •์ฑ… (์›น ๋งค๋งค)** โ€” ๊ฑฐ๋ถ€ยท๊ฒ€์ฆ ์—๋Ÿฌ๋Š” ํ† ์ŠคํŠธ๋งŒ, **๋งค๋งค๋“ฑ๋ก(submit_with_pin ์„ฑ๊ณต)ยท๋งค๋งค์ฒด๊ฒฐ(fill_watcher)๋งŒ** ํ…”๋ ˆ๊ทธ๋žจ. PIN ๋ฉ”์‹œ์ง€๋Š” **iMessage** (Apple ๋„๋ฉ”์ธ ๋ฐ”์ธ๋”ฉ `@stock.hyowons.net #PIN`, iOS Safari OTP ์ž๋™์ž…๋ ฅ). `handler.send_imessage_pin` (fire-and-forget Popen, AppleEvent timeout -1712 ๋– ๋„ ๋ฉ”์‹œ์ง€๋Š” ํ๋กœ). credential `credentials/admin_imessage.json` `{"handle": "01012345678"}`. ์ž๊ธฐ ์ž์‹  iMessage self-send ๊ฐ€๋Šฅ (mac โ†’ ๋ณธ์ธ iCloud handle). **์‹ ๊ทœ endpoint**: `/api/quote_book?code` (ka10004 ํ˜ธ๊ฐ€ 10๋‹จ๊ณ„ + ka10001 ํ˜„์žฌ๊ฐ€ + `nxt_enable`), `/api/order/check?code&account&side&price` (์ž”์•กยท๋ณด์œ ยทmax_qtyยท๋งค๋„ ์†์ต ๋ฏธ๋ฆฌ๋ณด๊ธฐ), `POST /api/order/propose` (handler.propose_trade wrapper, ์„ฑ๊ณต ์‹œ๋งŒ ํ…”๋ ˆ๊ทธ๋žจยทiMessage), `POST /api/order/verify` (handler.submit_with_pin wrapper, ์„ฑ๊ณต ์‹œ๋งŒ ํ…”๋ ˆ๊ทธ๋žจ), `POST /api/order/cancel` (handler.cancel_active_card, ํ…”๋ ˆ๊ทธ๋žจ X), `/api/order/active` (PinStore.peek + 4๊ณ„์ขŒ ๋ฏธ์ฒด๊ฒฐ ์นด์šดํŠธ), `/api/orders/open` (ka10075 4๊ณ„์ขŒ), `POST /api/orders/cancel?ord_no&account` (handler.cancel_open_order + ํ…”๋ ˆ๊ทธ๋žจ), `/api/market_state` (regular/nxt/closed/holiday/weekend phase), `/api/symbols/all` (๋ณด์œ +๊ด€์‹ฌ+๊ฐ์‹œ ํ†ตํ•ฉ dedup) **๊ธฐ์—…์ •๋ณด ๋ชจ๋‹ฌ (4ํƒญ)** โ€” ์ข…๋ชฉ ํ–‰ `๊ธฐ์—…์ •๋ณด` ๋ฒ„ํŠผ โ†’ `/api/stock_info?code`(ํ‚ค์›€ ka10001 + ๋„ค์ด๋ฒ„ ๋ถ„๊ธฐ์˜์—…์ด์ต + `fnguide_client` ํŽ€๋”๋ฉ˜ํ„ธยท์ปจ์„ผ์„œ์Šค + `wisereport_client` ๋ฆฌ๋น„์ „ยท์„œํ”„๋ผ์ด์ฆˆยท์ตœ๊ทผ๋ฆฌํฌํŠธ) fetch โ†’ ๋‹จ์ผ ์ข…๋ชฉ์€ 4ํƒญ ๋ Œ๋”: **์š”์•ฝ**(ํ˜„์žฌ๊ฐ€ยท๋ชฉํ‘œ์ฃผ๊ฐ€ยท๋ฆฌ๋น„์ „ยท์ฆ๊ฐ€์œจยท์„œํ”„๋ผ์ด์ฆˆ + ์ตœ์‹  ๋ฆฌํฌํŠธ 1๊ฑด) / **๊ธฐ์—…์ •๋ณดยท๊ฐ€์น˜**(PERยทPBRยทEPSยทBPSยทROEยท์˜์—…์ด์ตยท52์ฃผยท์œ ํ†ต๋น„์œจยท์™ธ๊ตญ์ธ) / **์„ฑ์žฅ์„ฑยท์ปจ์„ผ์„œ์Šค**(๋งค์ถœ/EPS ์ฆ๊ฐ€์œจยท๋ชฉํ‘œ์ฃผ๊ฐ€ยทํˆฌ์ž์˜๊ฒฌยท์ถ”์ •PERใ€”ํ˜„์žฌPER ๋ณ‘๊ธฐใ€•ยท๋ชฉํ‘œ๊ฐ€ ๋ฆฌ๋น„์ „ยท์„œํ”„๋ผ์ด์ฆˆ) / **ํˆฌ์ž๋ฆฌํฌํŠธ**(์ตœ๊ทผ ์ฆ๊ถŒ์‚ฌ ๋ฆฌํฌํŠธ ๋ชฉ๋ก, ๋ชฉํ‘œ๊ฐ€ โ–ฒ์ƒํ–ฅ/โ–ผํ•˜ํ–ฅ). ํƒญ์€ ๋ชจ๋‹ฌ ์ „์šฉ ํด๋ž˜์Šค(`.info-tabs/.info-tabbtn/.info-tabpanel` + `data-info-tab`/`data-info-panel`)์™€ ๋…๋ฆฝ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ž์‚ฐํƒญ `.sub-tab`๊ณผ ๊ฒฉ๋ฆฌ(์ „์—ญ restoreSubs ์ถฉ๋Œ ํšŒํ”ผ). ํ•ญ๋ชฉ๋ณ„ โ“˜ ํƒญ ํŒ์—… ์„ค๋ช…(`INFO_SPECS`/`INFO_DESC`, document ์œ„์ž„ `.info-label`). ๋น„๊ต ๋ชจ๋“œ(์—ฌ๋Ÿฌ ์ข…๋ชฉ)๋Š” ํƒญ ์—†์ด ๊ธฐ์กด ๋น„๊ตํ‘œ ์œ ์ง€. ETF ๋“ฑ ๋ฐ์ดํ„ฐ ์—†์œผ๋ฉด ํ•ด๋‹น ํƒญ "๋ฐ์ดํ„ฐ ์—†์–ด์š”". - `send_balance_to_budget.py` โ€” ๋งค์›” 1์ผ 04:30 launchd. ๋ณธ์ธ ๊ณ„์ขŒ(๊ฐ€ํฌ ์ œ์™ธ)๋ณ„ ์ž”์•กยท์˜ˆ์ˆ˜๊ธˆยทํ‰๊ฐ€์•ก์„ ์ง‘๊ณ„ํ•ด `agents/budget/inbox/incoming/`์— envelope(`topic: securities_balance`)๋กœ ๋–จ์–ด๋œจ๋ฆฐ๋‹ค. ๊ณจ๋”” ์›”๊ฐ„ ๊ฒฐ์‚ฐ(05:00) ์ž…๋ ฅ. LLM ๋ฏธ๊ฒฝ์œ , ์‹คํŒจ ์‹œ ๋ ˆ์ด ํ…”๋ ˆ๊ทธ๋žจ์œผ๋กœ ์ž๊ฐ€ ์•Œ๋ฆผ. - `trade_journal.py` โ€” ์ข…๋ชฉ๋ณ„ ๋งค๋งค ๊ธฐ๋ก ๋ˆ„์ . ํ‚ค์›€ REST์— ๊ธฐ๊ฐ„ ๊ฑฐ๋ž˜๋‚ด์—ญ API ๋ถ€์žฌ โ†’ ํ‰์ผ 21:00 launchd(NXT ์•ผ๊ฐ„ ๋งˆ๊ฐ ํ›„)๋กœ ka10170 4๊ณ„์ขŒ ํ˜ธ์ถœํ•ด `state/trade_journal.jsonl`์— ์ ์žฌ. `(date, account)` ๋‹จ์œ„ idempotent, ํœด์žฅ์ผ/์ฃผ๋ง self-skip(`--force`๋กœ ์šฐํšŒ). ์ ์žฌ ์‹œ์ž‘์ผ 2026-05-13 ์ด์ „ ๋ณด์œ ๋ถ„์€ `seed` ๋ช…๋ น(1ํšŒ)์œผ๋กœ ํ˜„์žฌ ํ‰๋‹จ๊ฐ€ร—(๋ณด์œ -๋‹น์ผ๋งค์ˆ˜+๋‹น์ผ๋งค๋„) ๋‹จ์ผ ํ–‰์œผ๋กœ ์••์ถ• ์ ์žฌ๋จ(`seed=true` ํ”Œ๋ž˜๊ทธยท`*` ๋งˆ์ปค). CLI: `collect [--date YYYYMMDD]` / `seed` / `show ` / `query [--from --to --account --code]` - `market_indicators_sync.py` โ€” ์‹œ์žฅ ๋‹จ์œ„ ADRยทํˆฌ์ž์ž๋ณ„ ๋งค๋งค ๋ˆ„์ . ๋„ค์ด๋ฒ„ m.stock `/api/index/{KOSPI|KOSDAQ}/integration` ํ•œ ์ฝœ๋กœ dealTrendInfoยทupDownStockInfo ์ˆ˜์ง‘ โ†’ `state/market_indicators_history.jsonl`. `(date, market)` ๋‹จ์œ„ idempotent, ํœด์žฅ/์ฃผ๋ง self-skip(`--force`). ํ‰์ผ 21:00 stock.trade-journal launchd์— ํ†ตํ•ฉ ๋ฐœํ™” (๋ณ„๋„ plist ์—†์Œ). KRX ์ •๋ณด๋ฐ์ดํ„ฐ์‹œ์Šคํ…œ์€ ์‘๋‹ต ํŒจํ„ด ๋ณ€๊ฒฝ์œผ๋กœ ๋ฐฑํ•„ ๋ณด๋ฅ˜ โ€” ๋งค์ผ ๋ˆ„์ ๋งŒ ์‹œ์ž‘. behive_web ์ž์‚ฐ์ •๋ณด ํƒญ์˜ ์‹œ์žฅ์ •๋ณด sub-tab์ด sparkline ๋ฐ์ดํ„ฐ์›์œผ๋กœ ์‚ฌ์šฉ - Portfolio data: `memory/portfolio.json` (v2 ์Šคํ‚ค๋งˆ ์ฐธ๊ณ ์šฉ ์Šค๋ƒ…์ƒท, `accounts.{์ผ๋ฐ˜,ISA}.positions`), `state/portfolio_daily_snapshot.json`, `state/kiwoom_tokens/{์ผ๋ฐ˜,ISA}.json`, `state/stock_codes.json`(ํ‚ค์›€ ka10099 lazy ์บ์‹œ), `state/watchlist_alerts.json`(์•Œ๋ฆผ ์ค‘๋ณต ๋ฐฉ์ง€), `state/ipo_calendar_sync.json`, `state/behive_*.json`, `state/fnguide_cache/{code}.json`(FnGuide ํŽ€๋”๋ฉ˜ํ„ธ 12h), `state/wisereport_cache/{code}.json`ยท`{code}_reports.json`(์ปจ์„ผ์„œ์Šค 12hยท๋ฆฌํฌํŠธ 6h), `state/stock_reports//`(๋ถ„์„ ๋ณด๊ณ ์„œ HTML) ## Scheduled Jobs OpenClaw ์ž๋™ํ™”๋Š” ๋‘ ๊ฐˆ๋ž˜๋กœ ๋™์ž‘ํ•œ๋‹ค (๋ชจ๋‘ Asia/Seoul): ### Cron (`cron/jobs.json`, OpenClaw ์—์ด์ „ํŠธ ์„ธ์…˜ โ€” LLM ๊ฒฝ์œ ) - **์˜ค์ „ ๋ธŒ๋ฆฌํ•‘** (main) โ€” Daily 07:00 โ€” ๋‰ด์Šค ๋ธŒ๋ฆฌํ•‘ ๋ฉ”์ผ - **์˜คํ›„ ๋ธŒ๋ฆฌํ•‘** (main) โ€” Daily 19:00 โ€” ๋‰ด์Šค ๋ธŒ๋ฆฌํ•‘ ๋ฉ”์ผ - **๋น„ํ•˜์ด๋ธŒ ์ข…๋ชฉ๋ถ„์„ ์š”์•ฝ** (stock) โ€” Weekdays 07/12/18์‹œ - **์›”๊ฐ„ ๊ฒฐ์‚ฐ** (budget) โ€” ๋งค์›” 1์ผ 05:00 โ€” ์ž์‚ฐ ๋ณ€๋™ ๋ฉ”์ผ + ๊ณจ๋”” ํ…”๋ ˆ๊ทธ๋žจ ### launchd (`~/Library/LaunchAgents/ai.openclaw.*.plist` โ€” LLM ๋ฏธ๊ฒฝ์œ , ์ง์ ‘ ์‹คํ–‰) - **gateway** โ€” ์ƒ์‹œ daemon (ํฌํŠธ 18789) - **claude-remote-control** โ€” on-demand daemon (์ฝ”๋”” ์„ธ์…˜, `claude-code-session` ์Šคํ‚ฌ์ด ๋„์›€) - **stock.behive-web** โ€” ์ƒ์‹œ daemon (์›Œ์น˜๋ฆฌ์ŠคํŠธ ์›น๋ทฐ, Tailscale 18790) - **stock.briefing** โ€” ํ‰์ผ 20:10 โ€” ์ผ์ผ ํฌํŠธํด๋ฆฌ์˜ค ๋ฆฌํฌํŠธ ๋ฉ”์ผ - **stock.briefing-fallback-2030** โ€” ํ‰์ผ 20:30 โ€” ์˜ค๋Š˜ ์Šค๋ƒ…์ƒท ์—†์œผ๋ฉด stock.briefing ์žฌ์‹คํ–‰ (idempotent) - **stock.briefing-fallback-2100** โ€” ํ‰์ผ 21:00 โ€” **๋ฌด์กฐ๊ฑด fresh fetch๋กœ ์Šค๋ƒ…์ƒท ๊ฐฑ์‹ ** (`briefing_fallback.py force` โ†’ ์Šค๋ƒ…์ƒท ์žˆ์œผ๋ฉด `stock_portfolio_report.py run` ๋ฉ”์ผยทํ…”๋ ˆ๊ทธ๋žจ X, ์—†์œผ๋ฉด `send` ํด๋ฐฑ + ์‹คํŒจ ์‹œ ์•Œ๋ฆผ). 20:10 ๋ฐ์ดํ„ฐ ๋ถ€์ •ํ™• ์ผ€์ด์Šค ๋ณด์™„์šฉ - **stock.watchlist-monitor** โ€” ํ‰์ผ 10:00 / 12:00 / 14:00 โ€” ์›Œ์น˜๋ฆฌ์ŠคํŠธ buy/target/stop ์•Œ๋ฆผ (2026-05-12: 15๋ถ„ ๊ฐ„๊ฒฉ โ†’ 3ํšŒ๋กœ ์ถ•์†Œ) - **stock.ipo-calendar-sync** โ€” ๋งค์ฃผ ๊ธˆ์š”์ผ 17:00 โ€” IPO ์ฒญ์•ฝยท์ƒ์žฅ ์ผ์ • ์บ˜๋ฆฐ๋” ๋“ฑ๋ก - **stock.holiday-sync** โ€” ๋งค์ฃผ ์ผ์š”์ผ 03:00 โ€” investing.com KRX ํœด์žฅ์ผ โ†’ `state/market_holidays.json` (behive_web ์ž๋™๊ฐฑ์‹  ํ† ๊ธ€์ด ์ฐธ์กฐ) - **stock.send-balance** โ€” ๋งค์›” 1์ผ 04:30 โ€” ๋ณธ์ธ ์ž”์•ก โ†’ ๊ณจ๋”” inbox (`securities_balance`) - **stock.trade-journal** โ€” ํ‰์ผ 21:00 โ€” EOD ๋ฐ์ดํ„ฐ ๋ˆ„์  ๋ฌถ์Œ. ProgramArguments๋Š” `/bin/sh -c` wrapper๋กœ ๋‘ ๋ช…๋ น sequential ์‹คํ–‰: โ‘ `trade_journal.py collect` (ka10170 4๊ณ„์ขŒ โ†’ `state/trade_journal.jsonl`) โ‘ก`market_indicators_sync.py collect` (๋„ค์ด๋ฒ„ m.stock KOSPI/KOSDAQ ADRยทํˆฌ์ž์ž๋ณ„ ๋งค๋งค โ†’ `state/market_indicators_history.jsonl`). ๋‘˜ ์ค‘ ํ•˜๋‚˜ ์‹คํŒจํ•ด๋„ ๋‹ค๋ฅธ ๊ฑด ์‹œ๋„. ๋กœ๊ทธ๋Š” `logs/stock-trade-journal.{log,err.log}` ํ•œ ๊ณณ์— ํ•ฉ์ณ์ง. - **budget.whooing-sync** โ€” ๋งค์‹œ 0/15/30/45๋ถ„ โ€” iMessage ๊ฒฐ์ œ๋ฌธ์ž โ†’ ํ›„์ž‰. ๋งค ์‚ฌ์ดํด ๋์— `gahee_reminder.run` ์ถ”๊ฐ€ ํ˜ธ์ถœ (๋งค์›” 25์ผ 10:00 KST ์ดํ›„ ๊ฐ€ํฌ๋‹˜๊ป˜ iMessage ๋ฆฌ๋งˆ์ธ๋” 1ํšŒ ๋ฐœ์‹  โ†’ ๋‹ต์‹  ํด๋ง โ†’ ํ…์ŠคํŠธ๋ฉด ํ›„์ž‰ `๊ฐ€ํฌ์ฃผ๋จธ๋‹ˆ` ์ฐจ์•ก ์ž๋™๋ถ„๊ฐœ, ์ด๋ฏธ์ง€๋ฉด ๊ณจ๋”” ํ…”๋ ˆ๊ทธ๋žจ ์•Œ๋ฆผ). ๋ณ„๋„ plist ์—†์Œ ## Agent Inbox Convention ์—์ด์ „ํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ hand-off๋Š” **ํŒŒ์ผ ๊ธฐ๋ฐ˜ inbox**๋กœ๋งŒ ํ•œ๋‹ค. LLM-to-LLM ์ž์—ฐ์–ด ํ†ต์‹ ์€ ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜ยทํ• ๋ฃจ์‹œ๋„ค์ด์…˜ ์ฆํญ ์œ„ํ—˜์ด ์žˆ์–ด ๊ธˆ์ง€. ### ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ (์ˆ˜์‹ ์ž ์†Œ์œ ) ``` agents//inbox/ โ”œโ”€ incoming/ โ† ์ƒˆ ๋ฉ”์‹œ์ง€ โ”œโ”€ processed/ โ† ์ฒ˜๋ฆฌ ์™„๋ฃŒ ํ›„ ์ด๋™ โ””โ”€ failed/ โ† ์ฒ˜๋ฆฌ ์‹คํŒจ (์Šคํ‚ค๋งˆ ์˜ค๋ฅ˜ยท๋ฏธ๋“ฑ๋ก topic ๋“ฑ) ``` ์ˆ˜์‹ ์ž๋Š” ์ž๊ธฐ inbox๋ฅผ ์ฑ…์ž„์ง„๋‹ค (์ •๊ธฐ ํด๋งยท์ฒญ์†Œยท๊ฐ์‚ฌ). ์†ก์‹ ์ž๋Š” `incoming/`์— ์“ฐ๋Š” ๊ฒƒ๊นŒ์ง€๋งŒ. ### Envelope (๋ถˆ๋ณ€ โ€” v1) ```json { "message_id": "uuid", "from": "stock", "to": "budget", "topic": "securities_balance", "created_at": "2026-04-26T20:10:00+09:00", "schema_version": 1, "payload": { ... } } ``` ํŒŒ์ผ๋ช…: `____.json` (์ •๋ ฌยท๊ฒ€์ƒ‰ ์šฉ์ด) ### ์›์น™ - **payload๋Š” ์ˆœ์ˆ˜ ๋ฐ์ดํ„ฐ** โ€” ์ž์—ฐ์–ด ์ง€์‹œ๋ฌธ ๊ธˆ์ง€ (ํ”„๋กฌํ”„ํŠธ ์ธ์ ์…˜ ์ฐจ๋‹จ) - **idempotency** โ€” ์ˆ˜์‹ ์ž๋Š” `message_id` ์ค‘๋ณต ์ฒ˜๋ฆฌ ์•ˆ ํ•จ - **์ƒˆ topic์€ `INBOX_TOPICS.md`์— ๋“ฑ๋ก ํ•„์ˆ˜** โ€” ๋ฏธ๋“ฑ๋ก topic์€ ์ž๋™ `failed/` - **์‘๋‹ต ํ•„์š” ์‹œ** โ€” ์ˆ˜์‹ ์ž๊ฐ€ ์†ก์‹ ์ž inbox์— ์ƒˆ ๋ฉ”์‹œ์ง€ ์ž‘์„ฑ (์–‘๋ฐฉํ–ฅ ack ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์—†์Œ) - **GC** โ€” `processed/`๋Š” 30์ผ ํ›„ ์ •๋ฆฌ, `failed/`๋Š” ์‚ฌ๋žŒ์ด ๊ฒ€ํ† ํ•ด์„œ ์ˆ˜๋™ ์‚ญ์ œ ์ƒ์„ธํ•œ topic ์Šคํ‚ค๋งˆ์™€ ์šด์˜ ๊ทœ์น™์€ `INBOX_TOPICS.md` ์ฐธ์กฐ. ### Cody Inbox (`agents/cody/inbox/`) ์ฝ”๋””๋Š” OpenClaw ์—์ด์ „ํŠธ๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ ํ•œ ๊ฐ€์ง€ ์˜ˆ์™ธ๋กœ inbox๋ฅผ ๊ฐ€์ง„๋‹ค. **์—์ด์ „ํŠธ๊ฐ€ ์ž์ฒด ๊ฐœ์„ ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ฝ”๋””์—๊ฒŒ ๊ฒ€์ฆยทํ›„์† ๊ฐœ์„  ์œ„ํƒ**ํ•˜๋Š” ๋‹จ๋ฐฉํ–ฅ ์ฑ„๋„์ด๋‹ค. - **ํ† ํ”ฝ:** `improvement_review` (์Šคํ‚ค๋งˆ๋Š” `INBOX_TOPICS.md`) - **์ž์—ฐ์–ด ํ—ˆ์šฉ ์˜ˆ์™ธ:** payload `summary`/`rationale`/`self_review_notes`/`concerns[].question`์€ ์ž์—ฐ์–ด OK. ๋‹จ "X ํ•ด์ค˜" ๋ฅ˜ ์ง€์‹œ๋ฌธ ๊ธˆ์ง€, ์‚ฌ์‹คยท๊ด€์ฐฐยท์šฐ๋ ค๋งŒ - **์ฒ˜๋ฆฌ ํ๋ฆ„:** 1. ์ฝ”๋”” ์„ธ์…˜ ๊ธฐ๋™ ์‹œ `incoming/` ๊ฐœ์ˆ˜๋งŒ ํ™•์ธ โ†’ 1๊ฐœ ์ด์ƒ์ด๋ฉด ํ•œ ์ค„ ์•Œ๋ฆผ ("๐Ÿ“ฅ ์ฝ”๋”” ์ธ๋ฐ•์Šค์— N๊ฐœ ์ฒ˜๋ฆฌ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค.") 2. **๊ด€๋ฆฌ์ž๋‹˜ ๋ช…์‹œ ์š”์ฒญ ์ „์—” ์ƒ์„ธ ๋ณด๊ณ ยท๊ฒ€์ฆยท๊ฐœ์„  ์‹œ์ž‘ X** โ€” ์ž๋™ ์ฒ˜๋ฆฌ ๊ธˆ์ง€ 3. ๊ด€๋ฆฌ์ž๋‹˜์ด "๊ฒ€์ฆ ํ ํ™•์ธํ•ด์ค˜" ๋“ฑ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ๋•Œ envelope ์ƒ์„ธ ์š”์•ฝ โ†’ ์šฐ์„ ์ˆœ์œ„ ์œ„์ž„ 4. ์ฝ”๋””๊ฐ€ `changed_paths` ๊ฒ€์ฆ โ†’ ํ•„์š” ์‹œ ์ง์ ‘ ๊ฐœ์„  (์œ„ํ—˜ ์ž‘์—…์€ ๋ณ„๋„ ์ปจํŽŒ) 5. envelope์„ `processed/`๋กœ ์ด๋™, ๊ฐ™์€ basename + `_report.md`์— ๊ฒ€์ฆ ๊ฒฐ๊ณผยทํ›„์† ๊ฐœ์„ ยท์ž”์—ฌ ์œ„ํ—˜ ๊ธฐ๋ก 6. **GC (๊ฐ™์€ ์‹œ์ ):** `processed/`์˜ mtime 7์ผ ์ดˆ๊ณผ ํ•ญ๋ชฉ์„ `trash`๋กœ ์ •๋ฆฌ (envelope JSON + report MD ์ง์œผ๋กœ). `failed/`๋Š” ์†๋Œ€์ง€ ์•Š๋Š”๋‹ค 7. ์Šคํ‚ค๋งˆ ์œ„๋ฐ˜์€ `failed/`๋กœ ์ด๋™ ํ›„ ๊ด€๋ฆฌ์ž๋‹˜์—๊ฒŒ ๋ณด๊ณ  - **ํ—ฌํผ ๋ฏธ์ •:** ์†ก์‹ ์ธก์€ ์—์ด์ „ํŠธ๊ฐ€ ์ง์ ‘ envelope JSON ์ž‘์„ฑ. ํŒจํ„ด ๊ตณ์œผ๋ฉด ์ถ”ํ›„ ์ถ”์ถœ - **ํšŒ์‹  envelope ์—†์Œ:** ๊ฒฐ๊ณผ๋Š” `processed/`์˜ report ํŒŒ์ผ๋กœ๋งŒ ๋‚จ๋Š”๋‹ค. ์†ก์‹  ์—์ด์ „ํŠธ๊ฐ€ ํ›„์† ์‚ฌ์ดํด์—์„œ ์ง์ ‘ ์กฐํšŒ ## Communication Rules - Respond in Korean (ํ•œ๊ธ€) - Use polite speech (์กด๋Œ“๋ง) - Address the owner as ๊ด€๋ฆฌ์ž๋‹˜ - End responses with status on a new line: `[์ง„ํ–‰์ค‘]` or `[๋‹ต๋ณ€์™„๋ฃŒ]` - Keep responses short, action-oriented, result-first - Avoid unnecessary explanation โ€” How > Why - Use `trash` over `rm` for deletions ## Coding Behavior Rules LLM ํ”ํ•œ ์‹ค์ˆ˜๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•œ ํ–‰๋™ ๊ทœ์น™. ์‚ฌ์†Œํ•œ ์ž‘์—…์€ ํŒ๋‹จ์œผ๋กœ ์ƒ๋žต ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ถˆํ™•์‹คํ•˜๋ฉด caution ์ชฝ์œผ๋กœ ๊ธฐ์šด๋‹ค. ### 1. ์ฝ”๋”ฉ ์ „์— ์ƒ๊ฐ (Think Before Coding) **๊ฐ€์ •ํ•˜์ง€ ๋ง๊ณ , ํ˜ผ๋™์„ ์ˆจ๊ธฐ์ง€ ๋ง๊ณ , ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ๋“œ๋Ÿฌ๋‚ผ ๊ฒƒ.** - ๊ฐ€์ •์€ ๋ช…์‹œ์ ์œผ๋กœ ๋งํ•œ๋‹ค. ๋ถˆํ™•์‹คํ•˜๋ฉด ์งˆ๋ฌธํ•œ๋‹ค. - ํ•ด์„์ด ์—ฌ๋Ÿฌ ๊ฐœ๋ฉด ์ „๋ถ€ ์ œ์‹œํ•œ๋‹ค โ€” ์กฐ์šฉํžˆ ํ•˜๋‚˜ ๊ณ ๋ฅด์ง€ ์•Š๋Š”๋‹ค. - ๋” ๋‹จ์ˆœํ•œ ๊ธธ์ด ๋ณด์ด๋ฉด ๋จผ์ € ๋งํ•œ๋‹ค. ์ •๋‹นํ•˜๋ฉด ๋ฐ˜๋ฐ•ํ•œ๋‹ค. - ๋ชจํ˜ธํ•˜๋ฉด ๋ฉˆ์ถ˜๋‹ค. ๋ฌด์—‡์ด ํ—ท๊ฐˆ๋ฆฌ๋Š”์ง€ ์ด๋ฆ„ ๋ถ™์ด๊ณ  ๋ฌป๋Š”๋‹ค. (์„ ํƒ์ง€๋Š” `AskUserQuestion`) ### 2. ๋‹จ์ˆœํ•จ ์šฐ์„  (Simplicity First) **๋ฌธ์ œ๋ฅผ ํ‘ธ๋Š” ์ตœ์†Œ ์ฝ”๋“œ. ์ถ”์ธก์„ฑ ์ฝ”๋“œ ๊ธˆ์ง€.** - ์š”์ฒญ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๊ธฐ๋Šฅ X - 1ํšŒ์šฉ ์ฝ”๋“œ์˜ ์ถ”์ƒํ™” X - ์š”์ฒญ๋˜์ง€ ์•Š์€ "์œ ์—ฐ์„ฑ"ยท"์„ค์ • ๊ฐ€๋Šฅ์„ฑ" X - ์ผ์–ด๋‚  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ ๋Œ€๋น„ ์—๋Ÿฌ ํ•ธ๋“ค๋ง X - 200์ค„ ์ง  ๊ฒŒ 50์ค„๋กœ ์ค„๊ฒ ๋‹ค ์‹ถ์œผ๋ฉด ๋‹ค์‹œ ์“ด๋‹ค. ์ž๋ฌธ: "์‹œ๋‹ˆ์–ด ์—”์ง€๋‹ˆ์–ด๊ฐ€ ์ด๊ฑฐ ๊ณผ์„ค๊ณ„๋ผ ํ• ๊นŒ?" ๊ทธ๋ ‡๋‹ค๋ฉด ๋‹จ์ˆœํ™”. ### 3. ์™ธ๊ณผ์  ๋ณ€๊ฒฝ (Surgical Changes) **ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ฑด๋“œ๋ฆฐ๋‹ค. ์ž๊ธฐ๊ฐ€ ๋งŒ๋“  ์ž”์žฌ๋งŒ ์ •๋ฆฌํ•œ๋‹ค.** - ์ธ์ ‘ ์ฝ”๋“œยท์ฃผ์„ยทํฌ๋งท ์ž„์˜ "๊ฐœ์„ " ๊ธˆ์ง€ - ์•ˆ ๋ง๊ฐ€์ง„ ๊ฒƒ ๋ฆฌํŒฉํ† ๋ง ๊ธˆ์ง€ - ๋‹ค๋ฅด๊ฒŒ ํ•˜๊ณ  ์‹ถ์–ด๋„ ๊ธฐ์กด ์Šคํƒ€์ผ ์œ ์ง€ - ๋ฌด๊ด€ํ•œ dead code ๋ฐœ๊ฒฌํ•˜๋ฉด ๋ณด๊ณ ๋งŒ โ€” ์‚ญ์ œ X - ๋ณ€๊ฒฝ ๋•Œ๋ฌธ์— ์ƒ๊ธด import/๋ณ€์ˆ˜/ํ•จ์ˆ˜ orphan์€ ๋ณธ์ธ์ด ์ •๋ฆฌ - ์‚ฌ์ „ ์กด์žฌํ•˜๋˜ dead code๋Š” ์š”์ฒญ ์—†์ด ์‚ญ์ œ X ํ…Œ์ŠคํŠธ: ๋ณ€๊ฒฝ๋œ ๋ชจ๋“  ๋ผ์ธ์€ ๊ด€๋ฆฌ์ž๋‹˜ ์š”์ฒญ์— ์ง๊ฒฐ๋˜์–ด์•ผ ํ•œ๋‹ค. ### 4. ๋ชฉํ‘œ ๊ธฐ๋ฐ˜ ์‹คํ–‰ (Goal-Driven Execution) **์„ฑ๊ณต ๊ธฐ์ค€์„ ์ •์˜ํ•˜๊ณ , ๊ฒ€์ฆ๋  ๋•Œ๊นŒ์ง€ ๋ฃจํ”„.** - "validation ์ถ”๊ฐ€" โ†’ "์ž˜๋ชป๋œ ์ž…๋ ฅ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ โ†’ ํ†ต๊ณผ์‹œํ‚ค๊ธฐ" - "๋ฒ„๊ทธ ๊ณ ์ณ" โ†’ "์žฌํ˜„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ โ†’ ํ†ต๊ณผ์‹œํ‚ค๊ธฐ" - "X ๋ฆฌํŒฉํ† ๋ง" โ†’ "์ „ํ›„ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ํ™•์ธ" ๋‹ค๋‹จ๊ณ„ ์ž‘์—…์€ ์งง์€ plan์„ ๋จผ์ € ๋งํ•œ๋‹ค: ``` 1. [๋‹จ๊ณ„] โ†’ ๊ฒ€์ฆ: [ํ™•์ธ] 2. [๋‹จ๊ณ„] โ†’ ๊ฒ€์ฆ: [ํ™•์ธ] ``` ๊ฐ•ํ•œ ์„ฑ๊ณต ๊ธฐ์ค€์€ ๋…๋ฆฝ์  ๋ฃจํ”„๋ฅผ ๊ฐ€๋Šฅ์ผ€ ํ•˜๊ณ , ์•ฝํ•œ ๊ธฐ์ค€("๋™์ž‘ํ•˜๊ฒŒ")์€ ๋Š์ž„์—†๋Š” ๋ช…์„ธ ์š”์ฒญ์„ ๋ถ€๋ฅธ๋‹ค.