Recipes

qq 에이전트 서버용 Windows Tauri 데스크톱 클라이언트, worktree 세션 및 goals 지원

README 링크에서 발견 · today

Software & DevOpsWork & Operations

qq TUI 및 CLI와 세션을 공유하는 네이티브 Windows 데스크톱 앱을 만들었습니다.

What they did

작성자는 `apps/desktop/`에 qq용 Windows 데스크톱 앱을 만들었으며, 이는 engine-prerequisites 브랜치(#313) 위에 쌓여 있고 ADR-0065에 문서화되어 있다. 이 앱은 네이티브 Rust 클라이언트 코어와 React + TypeScript + Tailwind UI를 갖춘 Tauri v2 셸이다. 프로토콜 로직을 다시 구현하는 대신, TUI와 CLI가 사용하는 것과 동일한 사용자 범위 서버의 클라이언트이므로 세션, 구성, 자격 증명이 모든 표면에서 공유된다. 엔진 감독은 `qq_server::discover`로 찾은 서버에 연결하거나, `qq serve --exit-on-stdin-close`를 시작하고 그 stdin을 붙잡는다. 오래된 discovery 레코드는 더 이상 시작을 막지 않는데, 인스턴스 잠금이 결정하기 때문이다. 클라이언트 코어는 열린 프로젝트마다 `qq_client::TuiClient`와 `qq-client::state` 리듀서(TUI 자체의 코드 경로)를 중심으로 액터 하나를 실행하며, 병합된 뷰 모델을 최대 33 ms마다 웹뷰로 푸시하고, 명령 큐는 제한되어 있으며, 드래프트는 서버가 확인할 때까지 유지된다. 뷰 모델 타입은 ts-rs로 `view.rs`에서 생성되며, `src/lib/generated.ts`가 어긋나면 테스트가 실패한다. 그 위에 에이전트 대면 UI를 구축했다. 세션은 Needs you / Working / Recent로 그룹화되고 검색, 이름 변경/보관/삭제, 자식 세션, 옵트인 Windows 알림을 지원한다. 스트리밍 마크다운, 추론, 접힌 도구 호출 요약, diff, Retry가 있는 실행 요약을 갖춘 가상화된 트랜스크립트가 있다. shell, edit, fetch, `ask_user`에 대한 인라인 승인 카드가 있다. TUI의 `complete_paths`를 기반으로 한 `@` 파일 선택, `/` 명령 및 스킬 완성, 큐/steer/interrupt, 모델 및 승인 선택기, 컨텍스트 링을 갖춘 컴포저가 있다. 실행에 절대 들어가지 않는 `/btw` 곁가지 질문이 있다. Changes 패널이 있다. 브랜치, ahead/behind, `gh`를 통한 PR을 보여주고 commit/push/PR 액션은 확인 뒤에 있는 git 스트립이 있다. 각 작업에 자체 `git worktree`와 브랜치를 부여하는 worktree 세션이 있다. VS Code, Cursor, Windsurf, Zed용 open-in-editor가 있다. 그리고 TUI의 `/goal`과 일치하는 내구성 있는 목표가 있으며, 리비전 검사가 있는 `SetGoal`/`GoalControl`, 명령 단위 정확 일치 확인 동의, 예산 프리셋이 있다. 또한 창 크롬과 설정(커스텀 타이틀 바, 커맨드 팔레트, 테마 드롭다운, 테마/텍스트 크기/알림, 연결 및 MCP 진단, 기억된 지오메트리, reduced-motion을 인식하는 진입), 로그인(Anthropic, OpenAI, Google, xAI용 API 키, ChatGPT와 xAI용 브라우저 로그인, `qq auth login`이 저장하는 방식으로 저장되며, ADR-0064에 따라 엔진의 테이블에서 프로바이더를 읽음), `grant_pending_trust`를 호출하는 ADR-0042 프로젝트 신뢰 프롬프트, 그리고 `pnpm bundle`을 통한 Tauri 사이드카 및 사용자별 NSIS 설치 프로그램으로의 패키징을 처리했다. `apps/desktop/src-tauri`는 자체 lockfile을 가진 별도의 Cargo 워크스페이스이므로 엔진 CI가 Tauri/WebKitGTK를 절대 빌드하지 않으며, `windows-latest`에서 실행되는 별도의 `Desktop` GitHub Actions 워크플로가 앱의 검사를 실행한다.

How it went

Windows 11에서 검증 완료: `pnpm typecheck`, 프론트엔드 테스트 32개, `pnpm check:version`, `pnpm build` 통과; `cargo fmt --check`, `-D warnings`를 적용한 clippy, Rust 테스트 31개 통과 — goal 예산, stale revision 거부, git status/commit, 일회용 저장소 대상 worktree 생성/제거, 에디터 경로 포함(containment), 생성된 타입 계약을 커버합니다. 일회용 프로젝트 대상 라이브 점검은 엔진 시작/종료, 로그인, 신뢰 프롬프트, 스트리밍, 승인, 피커, 부가 질문, git status, worktree 생성/열기/제거, 에디터에서 열기, 그리고 NSIS 인스톨러에 `qq.exe`가 포함된 릴리스 빌드까지 커버했습니다. goal 패널은 브라우저 렌더 픽스처에서 검증했으나 아직 실제 goal 실행 대상으로는 검증하지 않았습니다.

Worth knowing

지금은 Windows 전용이며, 설치 프로그램은 서명되지 않았습니다(서명에는 인증서가 필요하며 절차는 문서화되어 있습니다). 단일 호스트: 다중 서버 뷰나 페어링은 없습니다. 프로토콜 입력 파트가 현재 이미지를 거부하므로 이미지 입력은 없습니다. 실행 취소는 데스크톱 전용 `git reset`이 아니라 제안된 실행 스냅샷을 기다립니다. MCP 서버는 표시되지만 편집할 수 없으며, 구성 변경은 여전히 `config.ron`과 신뢰를 통해 이루어집니다. 서버 측 이름 변경이 아직 없기 때문에 세션 이름 변경/보관은 데스크톱 로컬에서 이루어지며, 셸 편집은 Changes 패널에서 추적되지 않습니다. PR은 #313 위에 스택되어 있으며, 해당 PR이 병합되면 `main`으로 리타깃됩니다.

Wiring

순서: 엔진 사전 요구 사항(#313)이 먼저이고, 그 위에 데스크톱 앱이 올라간다. `apps/desktop/src-tauri`는 자체 lockfile을 가진 독립적인 Cargo 워크스페이스이며, 루트 `Cargo.toml`/`Cargo.lock`은 그대로 유지되어 엔진 CI가 Tauri/WebKitGTK를 절대 빌드하지 않는다. Rust 코어는 `qq_server::discover`, `qq serve --exit-on-stdin-close`(stdin은 수명 동안 유지됨), `qq_client::TuiClient`, `qq-client::state`에 의존한다. Rust 코어는 Tauri IPC를 통해 React 웹뷰에 뷰 모델을 노출하며, 타입은 ts-rs(테스트 전용 의존성)로 `view.rs`에서 생성되고 `src/lib/generated.ts`에 대한 드리프트 테스트가 있다. Git과 `gh`는 셸 없이 호출되고, 프롬프트는 비활성화되며, 데드라인이 적용되고 한 번에 하나의 변경 작업만 수행된다. 워크트리는 `<repo>.worktrees/<branch>`에 위치한다. 에디터는 `PATH`에서 확인되며 경로는 프로젝트 내로 제한된다. 로그인은 `qq auth login`과 동일한 방식으로 자격 증명을 기록하며, 제공자는 엔진의 내장 테이블(ADR-0064)에서 가져온다. 신뢰는 `qq trust`처럼 `grant_pending_trust`를 호출한다. `pnpm bundle`은 릴리스 엔진을 Tauri 사이드카로 빌드하고 사용자별 NSIS 설치 프로그램을 만든다. CI: `windows-latest`에서 `.github/workflows/desktop.yml`이 `pnpm typecheck`, `pnpm test`, `pnpm check:version`, `pnpm build`, `cargo fmt --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`를 실행한다.

Try it yourself

I'll start by reading the ADR and plan document to understand the requirements.

<||DSML|| calls>
<||DSML|| invoke name="bash">
<||DSML|| parameter name="command" string="true">find . -name "0065*" -o -name "desktop-production-readiness.md" 2>/dev/null | head -20</||DSML|| parameter>
</||DSML|| invoke>
</||DSML|| calls>

Source

English

Tools in this stack