Communitygithub.com

windows-desktop-e2e

Windows 네이티브 데스크톱 앱(WPF, WinForms, Win32/MFC, Qt)의 E2E 테스트를 위한 스킬입니다. pywinauto와 Windows UI Automation을 사용하며, 네이티브 데스크톱 앱의 E2E 테스트 작성 시 활용하세요.

windows-desktop-e2e란 무엇인가요?

이 스킬은 pywinauto와 Windows UI Automation(UIA) API를 기반으로 Windows 네이티브 데스크톱 애플리케이션의 종단간(E2E) 테스트를 작성하고 실행하는 방법을 제공합니다. WPF, WinForms, Win32/MFC, Qt(5.x/6.x) 프레임워크를 지원하며, 각 프레임워크별 UIA 품질과 테스트 가능성 설정 방법을 안내합니다. 페이지 객체 모델(Page Object Model) 아키텍처를 권장하며, 안정적인 AutomationId 기반 로케이터 전략(우선순위: AutomationId > Name > ClassName+index > XPath), 다양한 대기 패턴(wait_visible, wait_gone, wait_until), CI/CD 통합(GitHub Actions windows-latest)을 포함합니다. 또한 문제 진단을 위한 아티팩트 관리(스크린샷, 화면 녹화)와 플래키 테스트 처리를 위한 스텝별 추적(opt-in) 기능을 제공하며, 설정 파일(config.py)을 통해 환경 변수로 테스트 대상을 유연하게 구성할 수 있습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/windows-desktop-e2e

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Windows 데스크톱 E2E 테스트

pywinauto와 Windows UI Automation(UIA)을 사용한 Windows 네이티브 데스크톱 애플리케이션의 종단간(E2E) 테스트입니다. WPF, WinForms, Win32/MFC 및 Qt(5.x/6.x)를 포괄하며, Qt 관련 지침은 별도 섹션으로 제공됩니다.

활성화 시점

  • Windows 네이티브 데스크톱 애플리케이션용 E2E 테스트를 작성하거나 실행할 때
  • 데스크톱 GUI 테스트 스위트를 처음부터 설정할 때
  • 불안정하거나 실패하는 데스크톱 자동화 테스트를 진단할 때
  • 기존 앱에 테스트 용이성(AutomationId, 접근 가능한 이름)을 추가할 때
  • 데스크톱 E2E를 CI/CD 파이프라인(GitHub Actions windows-latest)에 통합할 때

사용하지 말아야 할 때

  • 웹 애플리케이션 → e2e-testing 스킬(Playwright) 사용
  • Electron/CEF/WebView2 앱 → HTML 레이어는 브라우저 자동화가 필요하며 UIA가 적합하지 않음
  • 모바일 앱 → 플랫폼별 도구(UIAutomator, XCUITest) 사용
  • 실행 중인 GUI가 필요 없는 순수 단위 또는 통합 테스트

핵심 개념

모든 Windows 데스크톱 자동화는 **UI Automation(UIA)**에 의존합니다. UIA는 Windows에 내장된 접근성 API입니다. 지원되는 모든 프레임워크는 Claude가 읽고 조작할 수 있는 속성을 가진 UIA 요소 트리를 노출합니다.

테스트 (Python)
    └── pywinauto (UIA 백엔드)
        └── Windows UI Automation API   ← Windows에 내장, 프레임워크에 무관
            └── 앱의 UIA 공급자        ← 각 프레임워크가 자체 공급자 제공
                └── 실행 중인 .exe

프레임워크별 UIA 품질:

프레임워크AutomationId신뢰도비고
WPF5/5우수x:Name이 AutomationId에 직접 매핑됨
WinForms4/5양호AccessibleName = AutomationId
UWP / WinUI 35/5우수Microsoft의 완전한 지원
Qt 6.x5/5우수접근성 기본 활성화; 클래스 이름이 Qt6*로 변경됨
Qt 5.15+4/5양호향상된 접근성 모듈
Qt 5.7–5.143/5보통QT_ACCESSIBILITY=1 필요; objectName 수동 설정
Win32 / MFC3/5보통컨트롤 ID 접근 가능; 텍스트 매칭 일반적

설정 및 전제 조건

# Python 3.8+, Windows 전용
pip install pywinauto pytest pytest-html Pillow pytest-timeout
# 선택 사항: 화면 녹화
# ffmpeg 설치 후 PATH에 추가: https://ffmpeg.org/download.html

UIA에 접근 가능한지 확인합니다.

from pywinauto import Desktop
Desktop(backend="uia").windows()  # 모든 최상위 창 나열

Accessibility Insights for Windows(무료, Microsoft 제공)를 설치하세요. 테스트를 작성하기 전에 UIA 요소 트리를 검사하기 위한 DevTools 역할을 합니다.

테스트 용이성 설정 (프레임워크별)

테스트를 작성하기 전에 모든 대화형 컨트롤에 안정적인 AutomationId를 부여하는 것이 가장 영향력 있는 작업입니다.

WPF

<!-- XAML: x:Name이 자동으로 AutomationId가 됨 -->
<TextBox x:Name="usernameInput" />
<PasswordBox x:Name="passwordInput" />
<Button x:Name="btnLogin" Content="Login" />
<TextBlock x:Name="lblError" />

WinForms

// 디자이너나 코드에서 설정
usernameInput.AccessibleName = "usernameInput";
passwordInput.AccessibleName = "passwordInput";
btnLogin.AccessibleName = "btnLogin";
lblError.AccessibleName = "lblError";

Win32 / MFC

// .rc 파일의 컨트롤 리소스 ID가 AutomationId 문자열로 노출됨
// IDC_EDIT_USERNAME -> AutomationId "1001"
// Name에는 SetWindowText 사용; 더 풍부한 지원을 위해 IAccessible 추가

Qt — 아래 전용 섹션 참조


페이지 객체 모델

tests/
├── conftest.py          # 앱 실행 픽스쳐, 실패 스크린샷
├── pytest.ini
├── config.py
├── pages/
│   ├── __init__.py      # 임포트에 필요
│   ├── base_page.py     # 로케이터, 대기, 스크린샷 헬퍼
│   ├── login_page.py
│   └── main_page.py
├── tests/
│   ├── __init__.py
│   ├── test_login.py
│   └── test_main_flow.py
└── artifacts/           # 스크린샷, 비디오, 로그

base_page.py

import os, time
from pywinauto import Desktop
from config import ACTION_TIMEOUT, ARTIFACT_DIR

class BasePage:
    def __init__(self, window):
        self.window = window

    # --- 로케이터 (우선순위 순) ---

    def by_id(self, auto_id, **kw):
        """AutomationId — 가장 안정적. 첫 번째 선택으로 사용."""
        return self.window.child_window(auto_id=auto_id, **kw)

    def by_name(self, name, **kw):
        """표시 텍스트 / 접근 가능한 이름."""
        return self.window.child_window(title=name, **kw)

    def by_class(self, cls, index=0, **kw):
        """컨트롤 클래스 + 인덱스 — 취약하므로 가능하면 피할 것."""
        return self.window.child_window(class_name=cls, found_index=index, **kw)

    # --- 대기 ---

    def wait_visible(self, spec, timeout=ACTION_TIMEOUT):
        spec.wait("visible", timeout=timeout)
        return spec

    def wait_gone(self, spec, timeout=ACTION_TIMEOUT):
        spec.wait_not("visible", timeout=timeout)
        return spec

    def wait_window(self, title, timeout=ACTION_TIMEOUT):
        """새로운 최상위 창(대화상자, 자식 창)을 기다립니다."""
        dlg = Desktop(backend="uia").window(title=title)
        dlg.wait("visible", timeout=timeout)
        return dlg

    def wait_until(self, fn, timeout=ACTION_TIMEOUT, interval=0.3):
        """임의 조건을 폴링합니다. UIA 이벤트가 신뢰할 수 없을 때 사용합니다."""
        deadline = time.time() + timeout
        while time.time() < deadline:
            try:
                if fn():
                    return True
            except Exception:
                pass
            time.sleep(interval)
        raise TimeoutError(f"{timeout}초 내에 조건이 충족되지 않았습니다")

    # --- 액션 ---

    def click(self, spec):
        self.wait_visible(spec)
        spec.click_input()

    def type_text(self, spec, text):
        self.wait_visible(spec)
        ctrl = spec.wrapper_object()
        try:
            ctrl.set_edit_text(text)
        except Exception as e:
            # Qt 5.x 대체: UIA 값 패턴이 불완전할 수 있음
            import sys, pywinauto.keyboard as kb
            print(f"[windows-desktop-e2e] set_edit_text 실패 ({e}), 키보드 대체 사용", file=sys.stderr)
            ctrl.click_input()
            kb.send_keys("^a")
            kb.send_keys(text, with_spaces=True)

    def get_text(self, spec):
        ctrl = spec.wrapper_object()
        for attr in ("window_text", "get_value"):
            try:
                v = getattr(ctrl, attr)()
                if v:
                    return v
            except Exception:
                pass
        return ""

    # --- 아티팩트 ---

    def screenshot(self, name):
        os.makedirs(ARTIFACT_DIR, exist_ok=True)
        path = os.path.join(ARTIFACT_DIR, f"{name}.png")
        self.window.capture_as_image().save(path)
        return path

login_page.py

from pages.base_page import BasePage

class LoginPage(BasePage):
    @property
    def username(self): return self.by_id("usernameInput")

    @property
    def password(self): return self.by_id("passwordInput")

    @property
    def btn_login(self): return self.by_id("btnLogin")

    @property
    def error_label(self): return self.by_id("lblError")

    def login(self, user, pwd):
        self.type_text(self.username, user)
        self.type_text(self.password, pwd)
        self.click(self.btn_login)

    def login_ok(self, user, pwd, main_title="Main Window"):
        self.login(user, pwd)
        return self.wait_window(main_title)

    def login_fail(self, user, pwd):
        self.login(user, pwd)
        self.wait_visible(self.error_label)
        return self.get_text(self.error_label)

conftest.py

새 프로젝트의 경우 Tier 1 샌드박스 픽스쳐(아래 참조)를 선호합니다. 추가 비용 없이 파일 시스템 격리를 제공합니다. 이 기본 픽스쳐는 최소/레거시 설정에만 사용하세요.

import os, pytest
os.environ["QT_ACCESSIBILITY"] = "1"  # Qt 5.x UIA 지원에 필요

from pywinauto import Application
from config import APP_PATH, MAIN_WINDOW_TITLE, LAUNCH_TIMEOUT, ARTIFACT_DIR

@pytest.fixture
def app(request):
    if not APP_PATH:
        pytest.exit("APP_PATH 환경 변수가 설정되지 않았습니다", returncode=1)
    proc = Application(backend="uia").start(APP_PATH, timeout=LAUNCH_TIMEOUT)
    win  = proc.window(title=MAIN_WINDOW_TITLE)
    win.wait("visible", timeout=LAUNCH_TIMEOUT)
    yield win
    # 실패 시 스크린샷
    if getattr(getattr(request.node, "rep_call", None), "failed", False):
        os.makedirs(ARTIFACT_DIR, exist_ok=True)
        try:
            win.capture_as_image().save(
                os.path.join(ARTIFACT_DIR, f"FAIL_{request.node.name}.png")
            )
        except Exception:
            pass
    # 정상 종료 먼저 시도, 실패 시 강제 종료
    # proc는 pywinauto Application 객체 — wait_for_process_exit() 사용, wait_for_process() 사용 금지
    try:
        win.close()
        proc.wait_for_process_exit(timeout=5)
    except Exception:
        proc.kill()

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    setattr(item, f"rep_{outcome.get_result().when}", outcome.get_result())

config.py

import os
APP_PATH          = os.environ.get("APP_PATH", "")           # 환경 변수로 설정 — 기본 경로 없음
MAIN_WINDOW_TITLE = os.environ.get("APP_TITLE", "")
LAUNCH_TIMEOUT    = int(os.environ.get("LAUNCH_TIMEOUT", "15"))
ACTION_TIMEOUT    = int(os.environ.get("ACTION_TIMEOUT", "10"))
ARTIFACT_DIR      = os.path.join(os.path.dirname(__file__), "artifacts")

pytest.ini

[pytest]
testpaths = tests
markers =
    smoke: 주요 경로에 대한 빠른 스모크 테스트
    flaky: 알려진 불안정 테스트
addopts = -v --tb=short --html=artifacts/report.html --self-contained-html

로케이터 전략

AutomationId  >  Name (텍스트)  >  ClassName + 인덱스  >  XPath
  (안정적)         (가독성 좋음)       (취약함)           (최후의 수단)

Accessibility Insights로 검사 → 속성 창 → 먼저 AutomationId를 찾습니다.

# 런타임에 검사 — REPL에 붙여넣어 트리 탐색
win.print_control_identifiers()
# 또는 범위를 좁힘:
win.child_window(auto_id="groupBox1").print_control_identifiers()

대기 패턴

# 컨트롤이 나타날 때까지 대기
page.wait_visible(page.by_id("statusLabel"))

# 컨트롤이 사라질 때까지 대기 (예: 로딩 스피너)
page.wait_gone(page.by_id("spinnerOverlay"))

# 대화상자가 나타날 때까지 대기
dlg = page.wait_window("Confirm Delete")

# 사용자 정의 조건 (예: 텍스트 변경)
page.wait_until(lambda: page.get_text(page.by_id("lblStatus")) == "Ready")

기본 동기화 수단으로 time.sleep()을 절대 사용하지 마세요. 대신 wait() 또는 wait_until()을 사용하세요.

아티팩트 관리

# 요청 시 스크린샷
page.screenshot("after_login")

# 전체 화면 캡처 (창이 화면 밖에 있거나 최소화된 경우)
import pyautogui
pyautogui.screenshot("artifacts/fullscreen.png")

# ffmpeg를 사용한 화면 녹화 (테스트 전 시작, 후 중지)
import subprocess

def start_recording(name):
    return subprocess.Popen([
        "ffmpeg", "-f", "gdigrab", "-framerate", "10",
        "-i", "desktop", "-y", f"artifacts/videos/{name}.mp4"
    ], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def stop_recording(proc):
    proc.stdin.write(b"q"); proc.stdin.flush(); proc.wait(timeout=10)

단계별 추적 (옵트인)

기본 실패 스크린샷만으로는 불안정한 테스트를 진단하기에 충분하지 않은 경우가 많습니다. 아래 단계 수준 추적은 기본적으로 비활성화되어 있습니다. 불안정한 케이스를 재현할 때만 활성화하세요.

활성화

E2E_TRACE=1 pytest tests/test_login.py -v
# JSONL 로그에 입력된 텍스트 포함 (자격 증명/PII가 있는 테스트에는 사용하지 마세요):
E2E_TRACE=1 E2E_TRACE_INCLUDE_TEXT=1 pytest ...

BasePage에 패치

import os, json, time
TRACE_ENABLED      = os.environ.get("E2E_TRACE") == "1"
TRACE_INCLUDE_TEXT = os.environ.get("E2E_TRACE_INCLUDE_TEXT") == "1"

class BasePage:
    _step = 0

    def _trace(self, action, spec=None, text=None):
        if not TRACE_ENABLED:
            return
        BasePage._step += 1
        idx = f"{BasePage._step:03d}"
        os.makedirs(ARTIFACT_DIR, exist_ok=True)
        try:
            self.window.capture_as_image().save(
                os.path.join(ARTIFACT_DIR, f"step_{idx}_{action}.png"))
        except Exception:
            pass  # 캡처 실패가 테스트를 중단시켜서는 안 됨
        rec = {
            "ts": time.time(), "step": BasePage._step, "action": action,
            "locator": getattr(spec, "criteria", None),
            "text": text if TRACE_INCLUDE_TEXT else ("<redacted>" if text else None),
        }
        with open(os.path.join(ARTIFACT_DIR, "trace.jsonl"), "a") as f:
            f.write(json.dumps(rec) + "\n")

    def click(self, spec):
        self.wait_visible(spec); self._trace("click_before", spec)
        spec.click_input();      self._trace("click_after",  spec)

    def type_text(self, spec, text):
        self.wait_visible(spec); self._trace("type_before", spec, text)
        # ... 기존 set_edit_text / 키보드 대체 ...
        self._trace("type_after", spec)

주의 사항

  • PII / 자격 증명: type_text 내용은 기본적으로 <redacted>로 표시됩니다. 로그인이나 결제 흐름에서는 절대 E2E_TRACE_INCLUDE_TEXT=1을 설정하지 마세요.
  • 오버헤드: 액션당 약 50~200ms + 단계당 하나의 PNG 파일이 디스크에 생성됩니다. 기본 CI 매트릭스에서는 활성화하지 말고, 전용 불안정 재현 작업에서만 사용하세요.
  • 아티팩트 증가: 긴 흐름은 수십 MB를 생성할 수 있습니다. 이에 따라 retention-days를 조정하세요.
  • 병렬/재실행 위생: 이 간단한 예제는 trace.jsonl에 추가하고 클래스 수준 카운터를 사용합니다. 재실행 전에 아티팩트 디렉터리를 정리하고, 병렬 테스트에는 작업자별 아티팩트 디렉터리를 사용하세요.
  • 커버리지 격차: BasePage 외부에서 수행된 액션(테스트 코드의 원시 pywinauto 호출)은 추적되지 않습니다.

불안정한 테스트 처리

# 격리 — Playwright의 test.fixme()와 동일
@pytest.mark.skip(reason="불안정: 느린 CI에서 애니메이션 경쟁 상태. 문제 #42")
def test_animated_transition(self, app): ...

# 건너뛰기 (marker가 이미 "flaky"로 정의됨)
@pytest.mark.flaky(reruns=2, reruns_delay=5)
def test_unstable_grid_sort(self, app): ...

CI 통합 (GitHub Actions)

name: Desktop E2E
on: [push, pull_request]

jobs:
  e2e:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - name: 의존성 설치
        run: pip install pywinauto pytest pytest-html pillow
      - name: 내장 .exe 빌드 (또는 다운로드)
        run: dotnet build src/MyApp.sln -c Release
      - name: E2E 테스트 실행
        env:
          APP_PATH: src/MyApp/bin/Release/MyApp.exe
          APP_TITLE: My Application
        run: pytest tests/ -v
      - name: 아티팩트 업로드
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: e2e-artifacts
          path: tests/artifacts/

Qt 전용 섹션

Qt 애플리케이션(5.x 및 6.x)은 기본적인 프레임워크 중립 접근 방식 외에 특별한 주의가 필요합니다.

필수 설정

# Qt 5.x: pywinauto를 임포트하기 전에 설정
import os
os.environ["QT_ACCESSIBILITY"] = "1"

# Qt 6.x: 접근성이 기본적으로 활성화되어 있으며 env는 무시됩니다.
# UIA 플러그인은 Qt 6.3+에서만 지원됩니다.

Qt 5.x CLI 인수

# .exe에 전달:
MyApp.exe -platform windows:uiatrace=1

Qt 5.x 컴파일 시간 (선택 사항이지만 권장)

# .pro 파일:
QT += accessibility
DEFINES += QT_ACCESSIBILITY_SUPPORT

Qt 5.x 확인

test_app.py에 추가:

def test_uia_accessible(app):
    """QWidget에 접근 가능한 이름이 있고 UIA가 작동하는지 확인"""
    main = app.window()
    controls = main.descendants()
    # Qt는 고유한 프레임워크 클래스 이름을 사용함
    qt_classes = [c.class_name() for c in controls if "Qt" in c.class_name()]
    assert len(qt_classes) > 0, f"Qt 컨트롤 없음. QT_ACCESSIBILITY가 설정되었나요? 컨트롤: {[c.class_name() for c in controls[:10]]}"

Qt 5.x 제한 사항 (5.7–5.14)

  • objectName이 UIA AutomationId에 매핑되지 않음 → pywinauto 쿼리에서 auto_id= 대신 class_name= + found_index=를 사용해야 함
  • type_text의 set_edit_text가 실패 → **base_page.py의 type_text**가 키보드 대체로 자동 처리
  • 일부 Qt 위젯(예: QTableWidget 헤더)이 UIA 트리에 전혀 없음
  • 진행률 표시줄과 같은 표준 위젯은 UIA 값 패턴을 누락할 수 있음

Qt 5.x–6.x 차이점

측면Qt 5.xQt 6.x
접근성 기본값비활성화 (QT_ACCESSIBILITY=1 필요)활성화
클래스 이름Qt5* (예: Qt5QLineEdit)Qt6* (예: Qt6QLineEdit)
UIA 플러그인없음 (기본 MSAA/UIA)Qt 6.3+에서 Windows UI Automation 플러그인
objectName 매핑버전에 따라 다름 (대개 건너뜀)6.3+에서 부분적으로 지원

Qt 6.x 모범 사례

# Qt 6.x 더 안정적인 로케이터
user = main.child_window(class_name="Qt6QLineEdit", found_index=0)
# 또는 가능한 경우 class_name이 충분히 고유하면:
user = main.child_window(class_name="Qt6QLineEdit")

추가 리소스

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

관련 스킬