Imported from kochul2000/laymux (
AGENTS.md). Install upstream withnpx skills add kochul2000/laymux. Copyright stays with the author.
AGENTS.md
코딩 에이전트(Claude Code, Codex 등)를 위한 리포 진입점.
CLAUDE.md는 이 파일을 가리키는 포인터다 — 규칙은 한 곳(AGENTS.md)만 유지한다. 모든 출력은 한글로 한다.
이 리포
laymux — Tauri(Rust + WebView) 기반 자유 레이아웃 터미널 IDE. Windows·Linux 지원.
- src-tauri/ — Rust 백엔드. PTY, OSC 처리, Automation API(axum), 내장 MCP 서버, 설정/세션.
- ui/ — React + TypeScript + Zustand 프론트엔드. xterm.js 터미널, 그리드 레이아웃.
- lx — 터미널에 자동 주입되는 IDE 통신 CLI 바이너리(Rust, Tauri 동봉).
설계 정본 — 코드 짜기 전에 읽어라
설계 결정은 git issue 가 아니라 docs/ 가 SoT 다.
docs/architecture/— 현재 구조 (living doc, HEAD 반영)overview.md— 구조·기술 스택·레이아웃·Workspace/Layout 모델·View·SyncGroupdata-flow.md— Grid 편집·TerminalView(OSC 파이프라인·렌더러 reflow)·SelectorView 상태 계산·데이터 흐름·세션 영속api-contracts.md— Settings(settings.json)·Automation API+MCP·Rust 코드 설계 원칙(§14)·UI 코드 설계 원칙(§15)
docs/adr/— 아키텍처 결정 기록 (append-only, 불변). "왜 그렇게 정했나"docs/roadmap.md— 진행 상태docs/terminal/— 터미널 research 문서: 커서/플리커 정본 3종(ADR-0008) + Claude OSC 시퀀스 가이드 등 (인덱스)
living doc 동기화 의무. 기능 추가/수정 전 관련 docs/architecture/ 섹션을 읽는다. 코드가 서술과 어긋나면 같은 PR 에서 문서를 갱신하고, 구조가 달라지면 사용자와 논의한다.
PR 단위 ADR 게이트
ADR 작성 의무. 중요한 설계 결정은 git issue/PR 설명/채팅에만 남기지 않고 반드시 ADR 로 기록한다. PR 로 보낼 변경은 구현 계획을 세울 때와 PR 을 열기 직전에 ADR 필요 여부를 두 번 판정한다.
- 계획·최종 보고·PR 본문에
ADR: NNNN <링크>또는ADR 불필요: <구체적 이유>중 하나를 명시한다. 단순히 "작은 변경"이라고만 쓰는 것은 이유가 아니다. - 아래 기준 중 하나라도 해당하면 diff 크기와 무관하게 ADR 을 기본값으로 한다. 기존 ADR 의 직접 적용에 불과하면 새 ADR 대신 해당 ADR 을 링크하고, 결정 범위를 확장·정정하면 새 ADR 을 쓴다.
- 구현 전에 방향을 고정해야 하면 ADR 전용 PR 을 먼저 열어 결정부터 리뷰한다. 리뷰 중에는
Proposed, 방향 승인 후 머지할 때는Accepted로 전환한다. 이미 방향이 합의됐거나 구현 중 새 결정이 드러나면 구현 PR 에 ADR 을 포함하되, 코드 리뷰 전에 결정 문서를 먼저 읽을 수 있게 한다. - 구현 도중 소유권·외부 계약·불변식이 새로 생기면 코드/PR 설명에만 묻지 말고 같은 PR 의 ADR 과 living doc 을 먼저 갱신한 뒤 구현을 맞춘다.
- 리뷰할 때 ADR 기준에 해당하는데 ADR 또는 기존 ADR 링크가 없으면 누락을 blocking finding 으로 취급한다.
docs/adr/0000-template.md 를 다음 번호로 복사해 작성하고 인덱스(docs/adr/README.md)를 갱신한다. 병렬 PR 과 번호가 충돌하지 않도록 PR 직전 최신 main 기준으로 번호를 다시 확인한다. Accepted 된 ADR 을 번복할 때는 새 ADR 을 추가하고 옛 ADR 은 Superseded by 만 표시한다.
ADR 이 필요한 대표 기준:
- Automation/Remote/API/MCP/IPC 같은 외부 계약, 인증·권한·포트·CORS·네트워크 노출 정책을 바꾸는 경우
- PTY/OSC/터미널 렌더링, CWD 동기화, 세션 영속, 설정 스키마처럼 여러 모듈의 책임 경계를 바꾸는 경우
- 상태의 단일 진실원, 락 순서, 프로세스 실행, 크로스플랫폼 전략처럼 이후 구현 방향을 제한하는 경우
- 기존 ADR 과 충돌하거나 기존 ADR 을 확장·정정·폐기해야 하는 경우
단순 버그 수정, 지역적 리팩터, 테스트 보강, 문구 수정처럼 새 아키텍처 결정을 만들지 않는 변경은 ADR 없이 living doc/코드 주석/테스트로 충분하다. 판단이 애매하면 ADR 을 쓰는 쪽을 기본값으로 한다.
ADR 작성 아웃라인
ADR 은 구현 설명서가 아니라 결정과 근거의 불변 기록이다. 다음 순서로 작성한다.
- Metadata — Status(
Proposed→승인 후Accepted), Date, Source(사용자 요구·issue/PR·living doc 섹션·선행 ADR)를 적고, 확장/정정/폐기하는 ADR 이 있으면 관계를 명시한다. - Context — 해결할 문제, 현재 동작, 결정이 필요한 이유, 제약과 force, 범위와 비목표를 적는다. 코드 구조의 단순 나열은 living doc 으로 보낸다.
- Decision — 한 문장으로 요약 가능한 결론을 먼저 쓰고, 상태 소유권/SoT, 모듈 책임, 외부 계약, 불변식, 실패·보안·크로스플랫폼 정책을 단정형으로 구체화한다.
- Alternatives Considered — 실제 검토한 대안과 기각 이유를 적는다. 비용·복잡도·호환성·운영 위험 등 어떤 force 때문에 선택하지 않았는지 남긴다.
- Consequences — 장점뿐 아니라 비용, 부채, 위험, 마이그레이션/롤아웃, 테스트·문서 후속 작업, 결정을 재검토할 조건을 적는다.
구현 세부와 현재 파일 배치는 docs/architecture/에, 결정 이유와 장기 제약은 ADR 에 둔다. PR 에서는 ADR 의 Decision/Consequences와 코드·테스트·living doc 이 서로 일치하는지 함께 검증한다.
개발 환경
- 테스트는 TDD. 전체 스위트(unit + e2e + build + 실행 검증)는
/full-test스킬. - 화면(셀 격자) 테스트는 별도 스위트 — 실제 xterm 에 바이트를 흘려 셀을 읽는
*.screen.test.ts는cd ui && npm run test:screen으로만 돈다(기본vitest run에서 제외). "이 바이트를 흘리면 화면이 이렇게 된다" 류 주장은 mock 으로 쓰지 말고 여기에 쓴다. (ADR-0074, dev-repro-methodology.md §4.5) ui/의 npm 설치·테스트는 Windows 에서 돌린다 —ui/node_modules는 Windows 설치본이다(@rolldown/binding-win32-x64-msvc). WSL 에서npm install/npm audit fix를 돌리면 플랫폼별 네이티브 패키지가 섞여 다음vitest가Cannot find module '@rolldown/binding-linux-x64-gnu'로 기동조차 못 한다. 복구는 Windows 에서npm ci. WSL 에서 억지로 돌리면/mnt/d가 느려production-bundle.test.ts(실제 vite 빌드, 5s 기본 타임아웃)와vi.waitFor(기본 1s) 기반TerminalView테스트가 코드와 무관하게 깨진다. lockfile 자체는 플랫폼 독립이라 커밋해도 된다.- 컴파일 에러 우선 처리: 새 필드/기능으로 기존 테스트가 깨지면 기본값(
None/0)만 채워 컴파일만 통과시키지 말고, 그 기능을 실제 검증하는 e2e 테스트를 추가한다. - CI lint 없음 — 로컬에서 fmt/clippy/eslint/prettier 관리.
- target 자동 정리 —
cargo tauri dev|build의 before 커맨드가node scripts/sweep-target.mjs를 먼저 돌려 오래된 빌드 산출물을 치운다(하루 1회 throttle, 기본 10일 초과분).cargo-sweep이 설치돼 있으면 그걸 쓰고, 없으면incremental캐시만 지운다. 즉시 돌리려면npm run sweep:target, 끄려면LAYMUX_SWEEP_TARGET=0. 테스트는npm run test:sweep-target. - 마이그레이션 불필요 — 내부 개발 단계. 설정 경로/스키마 변경에 마이그레이션 로직을 만들지 않고 기존 데이터는 수동 처리.
자율 검증 루프
UI/디자인 변경은 /screenshot 스킬로 최종 결과를 확인한다. 스크린샷으로 못 보는 상태(모달 등)는 Automation API 엔드포인트를 확장해 프로그래밍적으로 트리거 후 검증한다. 기능 추가 시 항상 API 확장 + 자율 루프(API 조작→스크린샷→평가→수정) 구성 가능 여부를 고려한다. (api-contracts.md §12, ADR-0002)
- 포트 규칙: release=19280, dev=19281. 빌드 타입당 1 인스턴스. 개발 중 스크린샷/API 는 반드시 dev(19281), release(19280)는 사용자 소유이므로 건드리지 않는다. 인증 불필요(IP allowlist).
- 화면으로만 보이는 결함(키보드·IME·커서·렌더링)은 dev 에 재현 환경을 세팅하고 그 계층에서 측정한다 — 절차는
docs/dev-repro-methodology.md. 코드 독해만으로 진단 확정 금지. - 출력 부하·공정성 결함은
scripts/bench/의 결정적 다중 pane 플러드 벤치로 잰다 — 손으로 흘려서는 동시성이 재현되지 않는다. 실행법은 dev-repro-methodology.md §4.6. - dev 종료는 반드시
bash scripts/kill-dev.sh— release/dev 가 같은laymux.exe이므로tasklist | grep laymux로 수동 kill 금지.automation.json의port가 19281 일 때만 그 PID 를 믿고, 아니면 포트 19281 LISTENING 소유자로 폴백한다. 테스트는 discovery 파일의 실사용 경로를 절대 건드리지 않는다(임시 디렉터리 사용).
작업 규칙 (코드 짜기 전 확인)
- Rust 설계 원칙 — api-contracts.md §14. 에러
AppError(프로덕션unwrap()금지), 락MutexExt::lock_or_err()+state.rs순서, 매직 스트링은constants.rs, 파일 500줄↑ 분할,#[tauri::command]는 얇은 진입점(핵심 로직은&AppState내부 함수),eprintln!대신tracing. - 외부 프로세스는
crate::process::headless_command()—std::process::Command::new()금지(Windows 콘솔 창 깜빡임 방지). - OSC 처리는 Rust 전용 — 파싱(
osc.rs)·훅(osc_hooks.rs)·디스패치(dispatch_osc_action)는 Rust PTY 콜백 단일 패스. 프론트는 구조화 Tauri 이벤트만 구독. (ADR-0001) - CWD 는 SyncGroup 으로 — 백그라운드 셸을 만들지 말고
terminalStore의 syncGroup CWD 를 구독. FS 접근은 Ruststd::fs. (ADR-0003) - 원시 상태 분리 → 단일 계산 함수 — 여러 시스템이 한 표시에 관여할 때 각자 원시 상태만 저장하고, 표시는 계산 함수에서 도출. (ADR-0005)
- UI 설계 원칙 — api-contracts.md §15. CSS 변수 우선(
index.css:root), 호버는 CSS 클래스(style.background직접 조작 금지), 재사용 UI 는components/ui/, 키 조합 하드코딩 금지(키바인딩 레지스트리). - React 훅 패턴 — 렌더 본문에서
*Ref.current읽기·쓰기 금지(concurrent 렌더에서 찢어진다). 최신 props 미러링은useLayoutEffect(같은 커밋에서 동기 실행 → passive effect·DOM 이벤트보다 먼저), 외부 스토어 시딩+구독은useSyncExternalStore, 파생 가능한 값은 렌더 중 계산.react-hooks/refs·react-hooks/set-state-in-effect는error이며eslint-disable로 억제하지 않는다 — 억제가 필요해 보이면 규칙을 끄지 말고 패턴을 바꾸고, 정말 정당한 예외라면 근거를 api-contracts.md §15 에 남긴다.react-hooks/exhaustive-deps도error지만 여기는 예외가 실재한다(dep 를 의도적으로 좁히는 1회성 초기화 등) — 새로 억제하기 전에 패턴 변경을 먼저 시도하되, 남긴다면eslint-disable-next-line바로 위에 이유를 적는다. color-mix()금지 — html2canvas 가 파싱 못 해 스크린샷 API 가 깨진다.var(--accent-50)등 사전 정의 CSS 변수 사용. 상대 색상 문법(rgb(from …))도 같은 이유로 금지.- 색상은 색상 코드로 적는다 — 불투명은
#rrggbb, 반투명은 8자리#rrggbbaa.rgb()/rgba()채널 표기 금지(CSS·HTML·인라인 스타일·Rust 상수 모두). SGR·픽셀·OSC 처럼 채널이 형식 자체인 경계만 예외. (ADR-0112) - 터미널 커서/플리커는 research 정본을 따른다 — cursor/overlay/IME/flicker/DECSET 2026 변경은
docs/terminal/{fix-flicker,xterm-shadow-cursor-architecture,xterm-cursor-repaint-analysis}.md3개를 정본으로 확인. 기억·즉흥 실험만으로 수정 금지. 이 문서들은 통상 작업 중 수정하지 않는다(사용자가 명시 요청 시에만). (ADR-0008)
Claude Code 자동화 테스트
dev 터미널에서 claude 를 프로그래밍 방식으로 구동·검증하는 절차(초기화 대기 → trust 통과 → 타이틀 폴링 → 종료)는 docs/claude-code-automation.md 참조.