[English](README.md) | **한국어**

# Sidehand

**AI 에이전트는 뒤에서 Windows 앱을 만지고, 나는 하던 일을 계속합니다.**

Sidehand는 Claude Code, Codex, pi 같은 AI 에이전트가 Windows 데스크톱 앱을 읽고 조작하게 해 주는 도구입니다. 입력칸 채우기, 클릭과 우클릭, 드래그, 스크롤, 키와 단축키 입력, 앱 실행과 창 정리를 아무것도 앞으로 띄우지 않고 처리합니다. 그래서 게임을 하든 웹서핑을 하든 문서를 쓰든, 지금 쓰고 있는 창에서 키보드를 뺏기지 않습니다. Windows와 WSL 어디서든 쓸 수 있고, Windows에 기본으로 들어 있는 PowerShell만 있으면 됩니다.

![에이전트가 뒤에서 주문서를 채우는 동안 Notes 창에 계속 글자가 입력되는 모습](assets/demo.gif)

<sub>실제 Windows 11 화면을 [Recordly](https://www.recordly.dev)로 편집 없이 녹화했습니다([demo/record.ps1](demo/record.ps1)). 녹화하는 내내 왼쪽 Notes 창에 글자를 입력했는데, 145자가 하나도 빠짐없이 Notes에 들어갔고 맨 앞 창은 한 번도 바뀌지 않았습니다. [MP4로 보기](assets/demo.mp4)</sub>

## Sidehand를 쓰는 이유

- **포커스를 빼앗지 않습니다.** 실제 화면에서 WinForms, WPF, Edge, VS Code를 뒤에서 20번 조작하는 동안 내가 쓰던 창은 한 번도 밀려나지 않았습니다. 비교한 다른 도구들은 매번 다른 창이 앞으로 튀어나왔습니다.
- **토큰을 적게 씁니다.** 창 내용을 컨트롤 하나당 한 줄로 짧게 돌려줍니다. 같은 창을 읽을 때 다른 도구보다 토큰이 5~12배 적게 들었습니다.
- **가볍고 투명합니다.** 늘 켜져 있는 프로그램도, 사용 기록 전송도, 인터넷에서 받아 오는 실행 파일도 없습니다. 약 1,400줄의 PowerShell, C#, JavaScript가 전부이고, 처음 실행할 때 내 PC에서 직접 컴파일해 `%LOCALAPPDATA%\sidehand`에 넣어 둡니다. 코드를 다 읽어 보는 데 반나절이면 충분하고, 지울 때는 명령 한 줄이면 됩니다.

## 벤치마크

![Sidehand 벤치마크: 포커스 유지 10/10, 토큰 최대 12배 적음, 설치 크기 340배 이상 작음](assets/benchmark.ko.svg)

<sub>세 도구 모두 같은 Windows 11 PC와 같은 테스트 앱으로 측정했습니다. 포커스: Notes 창이 키보드를 가진 상태에서, 매번 새로 띄운 뒤쪽 WinForms, WPF 창을 조작했습니다([demo/record.ps1](demo/record.ps1)). 토큰: 같은 창을 한 번 읽은 결과를 `o200k_base` 토크나이저로 셌고, Cua Driver는 `include_screenshot: false`로 호출했습니다. 크기: npm 설치 후 크기, Cua Driver는 실행 파일 크기입니다. 그래프 원본: [demo/benchmark_chart.py](demo/benchmark_chart.py).</sub>

## 내가 일하는 동안 할 수 있는 것

✅는 맨 앞 창을 바꾸지 않고 실제로 반영된 것입니다. 실제 화면([demo/measure.ps1](demo/measure.ps1), [demo/record.ps1](demo/record.ps1))이나 숨은 테스트 데스크톱([test/isolated](test/isolated))에서 확인했고, 커서 위치를 추적한 항목에서는 커서도 한 번도 움직이지 않았습니다.

| | Win32, WinForms | WPF | Chromium (Edge, Chrome) | Electron (VS Code) |
|---|:-:|:-:|:-:|:-:|
| 입력, 클릭, 체크, 선택, 펼치기 | ✅ | ✅ | ✅ (`<select>` 드롭다운 제외) | ✅ 클릭 |
| 우클릭, 더블클릭, 휠, 호버 | ✅ | ❌ | ✅ | 측정 안 함 |
| 앱 안에서 드래그 | ✅ | ❌ | 측정 안 함 | 측정 안 함 |
| 키와 단축키 (`Enter`, `ctrl+a`) | ✅ | 허락 필요 | 허락 필요 | 허락 필요 |

앱 실행과 창 이동, 크기 조절, 최소화, 복원은 어떤 앱이든 같은 Windows 기능을 씁니다(WinForms로 확인). WPF는 진짜 마우스에서 오지 않은 마우스 메시지를 무시합니다. 또 WPF와 웹 페이지는 창에 포커스가 있을 때만 키를 받기 때문에, 이 경우에는 사용자 허락(`--allow-foreground`)이 필요합니다.

## 다른 도구와 비교

| | Sidehand | [Cua Driver](https://cua.ai/cua-driver) | [agent-desktop](https://github.com/vercel-labs/agent-desktop) | [Windows-MCP](https://github.com/CursorTouch/Windows-MCP) | Claude / OpenAI computer use |
|---|:-:|:-:|:-:|:-:|:-:|
| 내가 키보드를 쓰는 동안 뒤에서 작업 | ✅ | ⚠️ 터미널이 앞으로 옴 | ❌ 조작한 앱이 앞으로 옴 | ❌ 실제 마우스를 움직임 | ❌ 실제 마우스를 움직임 |
| 화면을 그림이 아닌 텍스트로 읽기 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 스크린샷 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 스크린샷 속 개인정보 가리기 (검게 칠하기, 흐림, 모자이크) | ✅ | ❌ | ❌ | ❌ | ❌ |
| 내 마우스를 건드리지 않고 우클릭, 더블클릭, 드래그, 키 입력 | ✅ | ✅ | ❌ `--headed` 필요 | ❌ | ❌ |
| 실제 마우스 입력 (내 마우스를 가져감) | ❌ 일부러 뺌 | ✅ | ✅ `--headed` | ✅ | ✅ |
| 웹 페이지 이동과 DOM 조작 | ❌ 버튼과 입력칸만 | ✅ CDP | agent-browser에 넘김 | ✅ DOM 모드 | ✅ |
| 백그라운드에 상주하는 프로그램 없음 | ✅ | ❌ 데몬 | ✅ | ❌ MCP 서버 | ❌ 실행 환경 필요 |
| macOS, Linux 지원 | ❌ | ✅ | ✅ | ❌ | ✅ |

<sub>첫 줄의 ⚠️와 ❌는 Cua Driver와 agent-desktop은 직접 측정한 결과이고, 나머지 두 도구는 공식 문서에 적힌 동작 방식을 따른 것입니다. agent-desktop은 `drag`, `hover`와 마우스 명령을 `--headed`에서만 실행한다고 문서에 적혀 있습니다. 개인정보 가리기의 ❌는 2026년 11월 기준 문서와 소스 코드에서 해당 기능을 찾지 못했다는 뜻입니다.</sub>

실제 마우스 입력은 일부러 넣지 않았습니다. Windows는 세션마다 커서가 하나뿐이라, 에이전트가 커서를 움직이는 순간 사용자는 마우스를 뺏깁니다. 합성 펜으로 "두 번째 포인터"를 만드는 방법도 시험했지만, Windows가 결국 커서를 옮겼고 WPF는 반응하지 않아서 넣지 않았습니다. macOS나 Linux, 실제 마우스 입력, 본격적인 브라우저 자동화가 필요하면 Cua Driver나 agent-desktop을 쓰세요. Windows에서 일하면서 에이전트가 내 작업을 방해하지 않기를 바라고, 직접 읽어 볼 수 있을 만큼 작은 도구를 원한다면 Sidehand가 맞습니다.

## 설치

```bash
# CLI: Claude Code, Codex 등 셸 명령을 실행하는 에이전트용 (Windows 또는 WSL, Node 18.3 이상)
npm install -g sidehand

# pi
pi install git:github.com/Blue-B/sidehand
```

CLI 에이전트라면 `AGENTS.md`나 `CLAUDE.md`에 다음 내용을 넣어 두세요.

```markdown
Use the `sidehand` CLI to read and operate Windows desktop apps without taking the user's focus.
Read with `sidehand tree "<window title>"`, act with `sidehand act <ref> <action>`, see `sidehand --help`.
Never pass `--allow-foreground` or `--bring-to-front` unless the user agreed.
```

## 사용법

```console
$ sidehand tree "Contoso Orders"
window: Contoso Orders  (18 elements)
  e4 Edit "Customer"
  e6 Edit "Quantity"
  e8 ComboBox "Region"
  e11 CheckBox "Express shipping" [Off]
  e12 Button "Submit order"

$ sidehand act e4 type "Blue-B Studio"
ok set text via window message: e4 Edit "Customer" value="Blue-B Studio"

$ sidehand act e8 select Jeju
ok select "Jeju" via window message: e8 ComboBox "Region" value="Jeju"

$ sidehand act e12 click
ok click via window message: e12 Button "Submit order"

$ sidehand wait "Contoso Orders" "Order submitted"
ok found in Contoso Orders
  e13 Text "Order submitted: Blue-B Studio, 3 units to Jeju, express"
```

| 명령 | 하는 일 |
|---|---|
| `sidehand windows`, `monitors` | 창과 모니터 목록 |
| `sidehand tree <제목> [--filter 글자]` | 창의 컨트롤을 텍스트로 출력하고 각각에 ref를 붙입니다. 긴 목록은 25개까지만 보여 줍니다 |
| `sidehand wait <제목> <글자> [--gone]` | 해당 글자가 들어간 컨트롤이 나타나거나 사라질 때까지 기다립니다 |
| `sidehand act <ref> <동작> [값]` | `click`, `type`, `check`, `uncheck`, `select`, `expand`, `collapse`, `scroll`, `right-click`, `double-click`, `hover`, `wheel down 3`, `drag <ref 또는 dx,dy>`, `press <키>` (`Enter`, `ctrl+a`, `"Down Down Enter"`), `focus` |
| `sidehand window <제목> <동작>` | `move x,y`, `resize w,h`, `minimize`, `restore` (내 창들 뒤로 복원), `close` |
| `sidehand launch <앱> [인자]` | 앱, 문서, URL을 포커스를 빼앗지 않고 실행 |
| `sidehand clipboard [텍스트]` | 클립보드 읽기, 또는 텍스트로 바꾸기 |
| `sidehand capture <모드>` | `full`, `region`, `monitor`, `active`, `window` (가려진 창도 캡처) |
| `sidehand crop`, `mask`, `resize`, `info` | 스크린샷 편집. `mask`로 공유 전에 원하는 영역을 검게 칠하거나 모자이크, 흐림 처리합니다 |

pi에서는 같은 기능을 `sidehand_ui_tree`, `sidehand_ui_act`, `sidehand_capture` 같은 도구로 씁니다.

## 할 수 없는 것

- `focus`와 최소화된 창 캡처는 내가 쓰던 창을 밀어내기 때문에, 에이전트가 `--allow-foreground`나 `--bring-to-front`를 붙여야만 실행합니다.
- 게임처럼 화면을 직접 그리는 앱은 컨트롤 정보가 없어 조작할 수 없습니다. 캡처는 됩니다.
- 최소화된 창은 `sidehand window <제목> restore`로 내 창들 뒤에 먼저 복원한 뒤 조작합니다.
- WPF 앱은 뒤에서 보낸 마우스 동작(우클릭, 더블클릭, 드래그, 휠)을 무시하고, 웹 페이지의 `<select>` 드롭다운은 뒤에서 고른 값을 받지 않습니다. 이때는 성공한 척하지 않고 실패로 알립니다.
- 두 앱 사이의 드래그 앤 드롭(파일을 폴더로 끌어 놓기 등)은 실제 마우스가 필요합니다.
- 키 입력은 자기 창 핸들을 가진 컨트롤로 갑니다. WPF와 웹 페이지는 `--allow-foreground`가 필요합니다.
- 관리자 권한으로 실행된 앱은 에이전트도 관리자 권한일 때만 메시지를 받습니다.

## 동작 방식

- `scripts/SidehandUia.cs`는 화면 낭독기가 쓰는 UI Automation COM 인터페이스를 직접 사용합니다. 처음 실행할 때 Windows PowerShell이 이 파일과, Windows의 `UIAutomationCore.dll`에서 만든 interop 어셈블리를 `%LOCALAPPDATA%\sidehand`에 한 번 컴파일해 둡니다.
- 조작할 때는 먼저 자기 창 핸들을 가진 일반 컨트롤(`EDIT`, `BUTTON`, `LISTBOX`, `COMBOBOX`)인지 확인하고 `WM_SETTEXT`, `BM_CLICK`, `LB_`/`CB_SETCURSEL` 메시지를 보냅니다. 창 메시지는 창을 활성화하지 않습니다.
- 그 밖의 컨트롤은 대상 창에 `WS_EX_NOACTIVATE`를 잠시 붙인 상태에서 UI Automation 패턴을 호출합니다. 그래서 앱이 스스로 `Focus()`를 불러도 앞으로 나오지 못합니다. 이 방법은 Cua Driver의 no-raise guard에서 가져왔습니다. 그래도 앱이 앞으로 나오면 결과에 경고를 표시합니다.
- 마우스 동작과 키는 요소 아래의 창에 메시지(`WM_RBUTTONDOWN`, `WM_MOUSEWHEEL`, `WM_KEYDOWN` 등)로 보냅니다. 앱의 메시지 처리 과정을 그대로 거치지만 내 커서는 움직이지 않습니다. 단축키를 보낼 때는 대상 앱의 키 상태에만, 그 순간에만 Ctrl, Shift, Alt를 누른 것으로 표시합니다.
- 모든 좌표를 실제 픽셀 기준으로 다루기 때문에, 화면 배율을 쓰는 모니터에서도 좌표와 스크린샷이 정확합니다.

## 측정 재현

```bash
npm test                          # 보이지 않는 데스크톱에서 실행, 화면에 아무것도 뜨지 않음, 몇 분 소요
powershell -File demo/measure.ps1 # 실제 화면: WinForms, Edge, VS Code에서 포커스와 커서 측정, 약 3분
powershell -File demo/record.ps1  # 실제 화면: 데모 영상과 세 도구 비교, 5~10분 동안 PC를 쓰지 않아야 함
```

`npm test`는 화면에 나타나지 않는 별도 Windows 데스크톱에 테스트 앱과 "사용자" 역할의 창을 띄우고, 종료되면 모든 프로세스가 함께 정리되도록 묶어서 실행합니다. Sidehand의 조작이 창을 하나라도 활성화하면 실패로 처리합니다. Cua Driver와 agent-desktop이 설치되어 있으면 함께 측정합니다. `demo/record.ps1 -Recordly`는 데모 영상을 다시 만들고, 중간에 누군가 PC를 쓰면 스스로 멈춥니다.

## 라이선스

MIT
