test-mlua-lsp

Installation
SKILL.md

mlua LSP Skill

MSW .mlua 작성/수정 시 @choigawoon/mlua-lsp@0.5.0npx로 호출해 코드 검증과 탐색을 수행한다.

프로토콜 범위 — 정직한 경계

이 스킬이 에이전트에게 노출하는 serve / mlua-batch.js / 단발 CLI는 LSP 프로토콜 자체가 아니라 데몬의 커스텀 newline-JSON RPC다. (1-based 좌표, 세션 없음, command 필드.) 내부에서 이 RPC는 진짜 LSP 서버(MSW 공식 msw.mlua 확장의 languageServer)를 구동한다.

진짜 LSP(JSON-RPC 2.0 + Content-Length + 0-based + initialize/didOpen/textDocument/*)가 필요하면 에디터가 쓰는 mlua-lsp server --stdio 엔드포인트를 직접 붙인다 — plugin.json의 lspServers가 이미 이 경로를 사용 중이다.

원칙

  • .d.mlua 파일을 직접 읽지 않는다. MSW SDK는 600+ 정의 파일이라 토큰 낭비. 대신 hover / complete로 조회.
  • 추측하지 말고 먼저 확인hover · complete · signature로 API를 본 뒤 작성.
  • 작성 후 반드시 diagnoseerrors: 0이 될 때까지 수정 루프.
  • 수정된 .mlua는 모아서 한 번에 batch diagnose — Edit/Write/MultiEdit로 .mlua를 건드렸다면, 해당 작업 단위(유저 턴 또는 연속된 수정 배치)가 끝나는 시점에 수정된 모든 파일을 단 한 번의 mlua-batch.js 호출로 묶어 diagnose한다. 파일마다 개별 호출 금지 — queries.jsonl에 한 줄씩 쌓아서 일괄 전송. 응답의 errors > 0인 파일만 수정 후 같은 방식으로 재진단. 자세한 절차는 "자동 점검 프로토콜" 참조.
  • 기본은 serve/batch (persistent stdin/stdout) — 한 번의 프로세스/TCP 연결로 N개 쿼리를 상각. 단발 CLI는 1개짜리 즉석 확인에만 사용.
  • 프로젝트 단위로 데몬 1개 — 다른 프로젝트로 넘어갈 때는 stop 호출. 유휴 30분 뒤 자동 종료(MLUA_LSP_IDLE_MINUTES로 조정).

프로젝트 루트 (project-root)

모든 명령의 첫 인자는 MSW 프로젝트 루트여야 한다.

  • RootDesk/MyDesk/Environment/NativeScripts/(.d.mlua)를 포함하는 상위 디렉토리
  • 잘못된 루트를 넘기면 hover/complete가 빈 결과를 반환하고 diagnose가 무의미해진다
  • 동일 루트로 일관되게 호출해야 데몬이 워크스페이스 인덱스를 재사용

예: /Users/you/msw-projects/MyGame/ (내부에 RootDesk/MyDesk/·Environment/ 존재)

기본 호출 — 배치 (serve)

# 파일로부터
node ~/.claude/skills/test-mlua-lsp/scripts/mlua-batch.js <project-root> queries.jsonl

# stdin에서
cat queries.jsonl | node ~/.claude/skills/test-mlua-lsp/scripts/mlua-batch.js <project-root>

요청 포맷 (newline-JSON, 한 줄 1쿼리)

{"id":1,"command":"diagnose","file":"/abs/path/a.mlua"}
{"id":2,"command":"hover","file":"/abs/path/b.mlua","line":10,"col":5}
{"id":3,"command":"complete","file":"/abs/path/c.mlua","line":7,"col":12}
  • line/col1-based. 생략 시 null.
  • id는 선택 (숫자 또는 문자열 모두 OK — 데몬은 opaque하게 echo back). 응답 상관관계 추적에 유용.
  • 응답도 newline-JSON으로 stdout에 순서대로 출력.

왜 batch가 기본인가

경로 1쿼리 N쿼리 (warm)
단발 CLI (mlua-lsp.js <cmd> …) ~22ms ~22ms × N (매 호출 Node + TCP)
mlua-batch.js (serve) ~22ms + α ~22ms + N × daemon-RPC (p50 <5ms)

에이전트가 여러 심볼을 연속 조회하거나 여러 파일을 진단할 때 batch가 수 배 빠르다. v4 벤치(MineSimulator 279파일, warm 6.2s @ 22ms 평균)는 단발 CLI 기준이므로, serve 사용 시 더 단축된다.

단발 호출 (fallback, 1쿼리)

한 지점만 확인하는 경우:

node ~/.claude/skills/test-mlua-lsp/scripts/mlua-lsp.js <command> <project-root> <file> [line] [col]
  • 래퍼가 npx -y @choigawoon/mlua-lsp@0.5.0을 통해 실행 — 별도 설치 불필요.
  • 출력은 pretty-printed JSON.
  • 데몬 실패 시 --no-daemon one-shot으로 자동 폴백.

명령

명령 용도 반환 핵심
diagnose 타입/문법/미존재 멤버 에러 { errors, warnings, diagnostics[] }
hover 해당 위치의 타입·설명 { type, documentation }
complete . 직후 멤버 목록 { summary, completions[] }
definition 정의 위치 이동 { locations[] }
references 사용 지점 찾기 { count, locations[] }
signature 함수 시그니처·파라미터 { signatures[] }
status 데몬 상태 { status, port, pid }
dump 데몬 live 스냅샷 (in-flight · 완료 카운트, --verbose로 open-file 목록) { inflight, completed, openFiles? }
describe 머신 리더블 커맨드 카탈로그 (데몬 없이도 동작) JSON 카탈로그
stop 데몬 종료
serve (raw) newline-JSON 프록시 (batch의 하위 계층) stdin→stdout JSONL

diagnose·status·dump·describe·stop은 위치가 불필요, 나머지는 line/col 필수.

⚠️ Cold-daemon false positive (FP) — 글로벌 'Symbol not found' 의 함정

증상: diagnose / hover / signature 가 다음 중 다수에 found: false 또는 "Symbol not found" (info severity) 를 반환:

  • _ 로 시작하는 서비스 글로벌: _SpawnService, _TimerService, _UserService, _GameLogic, _UtilLogic, _MapService, _HttpService, _DataStorageService, _InputService, …
  • 빌트인 타입 생성자: Vector2, Vector3, Quaternion, Color, Vector4
  • 빌트인 컴포넌트 타입: TextComponent, TransformComponent, UITransformComponent, …

원인: 데몬이 갓 떴거나 .d.mlua 파싱이 미완료 — 워크스페이스 인덱스 cold 상태. 진짜 미존재 아님.

왜 위험한가: 이 상태에서는 해당 글로벌의 member 호출 인자 타입 검증이 우회된다. 예 — _SpawnService:SpawnByModelId(id, name, Vector2(x,y), map) 처럼 4번째 위치에 Vector2 를 넘겨도 LSP 가 통과시킴. 런타임에 LEA-3005 InvalidArgument 가 터질 때까지 잡히지 않음. 실제 발생 이력 있음 (Vampire Survivor SpawnMonster, Vector3 자리에 Vector2 — LSP cold FP 로 묻힘).

처치 — Warm-up & re-diagnose 절차:

  1. WARMUP.mlua 편집을 시작하기 전, 또는 cold 의심 시점에 핵심 글로벌을 한 번씩 hover 로 두드린다. 한 번에 batch 로:

    cat <<EOF | node <plugin>/skills/test-mlua-lsp/scripts/mlua-batch.js <project-root>
    {"id":"w1","command":"hover","file":"<any project .mlua>","line":1,"col":1}
    {"id":"w2","command":"complete","file":"<any project .mlua>","line":1,"col":1}
    EOF
    

    직접 호출 대신 — 편집 대상 파일에 _SpawnService / Vector3 가 등장하는 줄·열로 hover 던지기. 응답이 found:false 라도 데몬이 그 시점부터 인덱싱을 시작. 30초~수분 후 재시도.

  2. RE-DIAGNOSE — warmup 후 동일 파일을 batch diagnose 재실행. cold 시 떴던 13개 "Symbol not found" 가 사라지고 진짜 진단(시그니처 미스매치 등) 만 남는다.

  3. 글로벌이 여전히 not-found 면 두 가지 경우:

    • Case A — 루트 잘못: <project-root>/Environment/NativeScripts/Service/ 가 비어있거나 없음. 데몬은 아무리 기다려도 글로벌을 못 찾는다. 정답: 올바른 워크스페이스 루트로 재시작.
    • Case B — 인덱서 sticky: mlua-lsp.js statusworkspaceLoaded: true 라고 보고하는데도 동일 FP 가 지속. 이는 데몬이 인덱스를 적재했다고 믿지만 실제 심볼 테이블이 비어있는 상태. mlua-lsp.js stop 으로 데몬 죽이고 재호출 시도. 그래도 동일하면 워크스페이스의 Environment/ 디렉토리 자체가 손상되었거나 버전 불일치. 이 경우 현재 턴에서 LSP 검증은 포기하고, 코드 리뷰(시그니처 수동 확인 — *.d.mlua 직접 grep) 로 대체. 사용자에게 "LSP 인덱서 sticky 상태 — 수동 검증으로 진행" 명시.

    판별 단서: 동일 워크스페이스의 기존 작동하는 .mlua 파일(예: UIPopup.mlua) 에 diagnose 던졌을 때도 같은 글로벌이 not-found 로 나오면 sticky/workspace 문제 (편집 파일 책임 아님).

식별 기준 — FP 인지 진짜인지:

토큰 모양 not-found 의미
_ 로 시작 (_SpawnService) 거의 항상 cold FP — warmup
Vector2/Vector3/Quaternion/Color 거의 항상 cold FP — warmup
대문자 시작 빌트인 컴포넌트 (TextComponent, TransformComponent) 거의 항상 cold FP — warmup
사용자 정의 모듈/스크립트명 (MyManager, VampireGameManager) 진짜 — 정의 누락 또는 오타
메소드/멤버 ('CurrentMap' not found in 'Entity') warmup 후에도 남아있으면 진짜 — 진짜 API 누락

금지 — "어차피 cold FP 일 거다" 로 진단 결과 통째 무시 후 ship. 반드시 warmup → 재진단으로 진짜 결과를 본 뒤 turn 마감.

워크플로

0. [WARMUP] 새 데몬 또는 의심 시 — 글로벌 hover 한 차례로 인덱스 깨우기
1. [PRE]    batch로 hover/complete/signature 묶어서 API 확인
2. [WRITE]  .mlua 작성 (여러 파일이어도 OK — 수정된 파일 경로를 머릿속에 쌓아둔다)
3. [POST]   수정된 전 파일을 한 번의 batch diagnose로 일괄 점검 → errors > 0인 파일만 수정 → 재진단 루프 → 전부 errors: 0
            ⚠️ diagnose 결과에 글로벌 'Symbol not found' 가 다수면 cold FP 의심 — warmup 후 재진단
4. [PLAY]   (선택) msw-maker-mcp play로 실행 검증

예: 한 심볼에 대해 hover + definition + references를 동시에 알고 싶을 때 세 번의 단발 호출 대신 한 번의 batch.

cat <<EOF | node ~/.claude/skills/test-mlua-lsp/scripts/mlua-batch.js /path/to/MyGame
{"id":1,"command":"hover","file":"/path/to/MyGame/RootDesk/MyDesk/Player.mlua","line":12,"col":18}
{"id":2,"command":"definition","file":"/path/to/MyGame/RootDesk/MyDesk/Player.mlua","line":12,"col":18}
{"id":3,"command":"references","file":"/path/to/MyGame/RootDesk/MyDesk/Player.mlua","line":12,"col":18}
EOF

자동 점검 프로토콜 — .mlua 편집 배치 끝에 반드시 수행

이 스킬을 로드한 에이전트는 다음 절차를 기본 동작으로 간주한다. Claude Code 훅이 자동 발화하지 않으므로, 스킬 지시를 따르는 에이전트가 스스로 수행해야 한다.

  1. 편집 트래킹 — Edit / Write / MultiEdit로 .mlua 파일을 수정할 때마다 그 절대경로를 세션 내에서 모아둔다 (중복 제거). 한 파일을 여러 번 고쳤다면 한 번만 카운트.
  2. 체크포인트 — 아래 중 하나에 도달하면 즉시 batch diagnose를 실행한다:
    • 유저 턴에 요청된 편집 작업을 모두 마쳤을 때 (응답을 마무리하기 직전)
    • 편집 파일 수가 10개를 넘어갈 때 (중간 점검)
    • 유저가 "점검", "diagnose", "검수" 등을 명시적으로 요청할 때
  3. 실행 — 수집한 경로 집합을 newline-JSON 쿼리로 조립해 단 한 번의 mlua-batch.js 호출로 전송.
    # 예: 수정된 파일 N개를 한 번에
    printf '%s\n' "${edited_files[@]}" \
      | awk '{printf "{\"id\":%d,\"command\":\"diagnose\",\"file\":\"%s\"}\n", NR, $0}' \
      | node /path/to/mlua-batch.js <project-root>
    
  4. 수정 루프 — 응답 중 errors > 0인 파일만 골라 diagnostics를 근거로 수정 → 해당 파일들만 다시 batch diagnose. 모든 파일이 errors: 0이 될 때까지 반복.
  5. 리포트 — 최종 결과를 유저에게 요약: 수정 파일 수 / clean 수 / 남은 에러 수 / (해결 불가한 경우) 원인 분석. 아무것도 수정하지 않았더라도 "편집 없음 — diagnose 생략"이라고 명시해 누락 여부를 투명하게.

금지 사항:

  • 파일마다 개별 mlua-lsp.js diagnose 호출 — TCP handshake × N으로 수 배 느려짐.
  • 편집 직후 매번 진단 — 연속 수정 흐름을 끊고 데몬 캐시를 낭비.
  • diagnose 생략 후 턴 종료 — 에이전트의 재량에 맡기지 말고 체크포인트에 도달하면 무조건 실행.

에디터 LSP 연동 (진짜 LSP 경로)

plugin.jsonlspServers.mlua 파일을 열면 자동으로 mlua-lsp server --stdio를 붙인다 (npx 기반, 설치 불필요). 이쪽이 실제 LSP 스펙을 따르는 경로 — 에디터는 Content-Length 프레이밍 + 세션 lifecycle로 통신한다. 본 스킬의 에이전트 검증 루프(커스텀 RPC)와는 독립된 LSP 인스턴스를 사용하므로 둘은 공존한다.

참고

Related skills
Installs
9
First Seen
Apr 21, 2026