v2.0.0 — 검색 엔진 교체, 저장소 재설계, UI 개편, 다국어, AI 에이전트 연동 - #21
Merged
Conversation
기존 구조를 유지한 채로는 고칠 수 없는 결함들이 서로 얽혀 있어 코어를
한 번에 재설계했다. 시니어 4인의 독립 분석에서 교차 확인된 항목이 근거다.
## 데이터 손실 및 크래시
- StdEntry.stableKey 에 type 을 포함. 기존 키는 TERM/WORD 가 약어를 공유하면
충돌했고(XCRD, ADDTX, RN 등 149쌍), 로더가 terms→words 순으로 map 에
덮어써서 WORD 가 TERM 의 도메인/포맷 메타데이터까지 가져갔다.
번들 7,200건 중 152건이 조용히 사라지던 것을 3건으로 줄였다.
남은 3건은 도메인 길이만 다른 동일 용어이며, 덮어쓰기 대신 병합해
synonyms 를 보존한다.
- 자동완성 삽입 문자열의 StringIndexOutOfBoundsException 수정.
prefix 의 인덱스를 무관한 suggestion 문자열에 적용하던 식이었고,
2자 이하 약어 51건 / 4자 이하 1,208건 때문에 'val userNm' 만 타이핑해도
재현됐다. 예외를 피한 경우에도 결과가 오염됐다(usrI + ipAddr → usrddr).
- 룩업의 표시 텍스트와 실제 삽입 문자열을 일치시켰다.
## 검색 (Trie → 역색인)
- 무매치가 존재하지 않던 문제: score() 가 타입 가중치를 무조건 더해
score > 0 필터가 아무것도 걸러내지 못했고, trie 미스는 전체 스캔으로
폴백했다. 오타 하나가 전체 사전을 파일 순서로 반환했다.
- 후보를 결과 limit 으로 자른 뒤에 점수를 매기던 순서를 뒤집었다.
상위 5개 요청이 "먼저 만난 5개"였다.
- 타입/도메인 필터를 절단 이후가 아니라 점수 계산 전에 적용. 매치가
있는데도 0건이 나오던 원인.
- norm() 이 '_' 를 제거해 USER_NM 이 usernm 한 덩어리로만 색인되던 문제를
서브토큰 역색인으로 해결. 이 표준의 핵심 접미어인 nm/ymd/cd/no/yn 이
검색 가능해졌다(nm: 972건 중 9건 → 대부분).
- 한글 초성 검색 추가. 미스가 전체 반환으로 이어지던 탓에 없는 것보다
나빴던 경로다.
- description 을 색인에서 제외. 전체 노드의 약 2/3 를 차지하면서 실효성이
없었고, 정의문 접두 매치가 실제 매치를 밀어냈다.
- 길이 정규화(coverage)와 IDF 도입. '사' 가 1음절과 12음절 항목에
동점을 주어 수백 건이 파일 순서로 정렬되던 문제, 그리고 일자(1,012건)
여부(614건) 번호(613건) 같은 초고빈도 접미어가 신호를 잠식하던 문제.
- 동점을 타입/삽입가능/길이/키 순으로 결정론적으로 해소.
- 편집거리를 약어에도 적용(기존엔 koName 전용이라 영문 쿼리에서 사실상
죽은 분기였다) + 길이 필터·밴딩·조기 종료로 비용 제한.
- checkCanceled 를 주입 가능하게 하여 취소가 실제로 동작하고, 인덱스는
플랫폼 없이 단위 테스트 가능하게 유지.
## NameGenerator
- acronyms/suffixes 집합 제거. capitalizeFirst() 가 뒷부분을 소문자화해
두 분기 결과가 항상 동일했고, suffixes 는 snake 경로에서만 참조돼
user_id_YN 같은 대소문자 혼합을 만들었다(번들 약어 1,230건 해당).
이 도메인의 실제 컨벤션은 약어를 단어로 취급하는 것이라 수정보다
제거가 맞다.
- tokenize 가 공백/마침표/하이픈을 분리하도록 수정. WORD 의 enName
"Reference Amount For One Serving" 이 공백 포함 식별자가 되던 문제.
- CONSTANT_CASE 추가. camel/snake/pascal 3종을 만들어 두고 camel 만 쓰고
버리던 것을 4종 모두 노출한다.
- 라틴 문자가 없으면 null 반환. DOMAIN 106건이 koName 폴백으로 '가격n10'
같은 한글 식별자를 소스에 삽입하던 것을 차단.
- 엔트리→식별자 정책이 4곳에 복붙돼 있던 것을 NameSuggestions 로 단일화.
## 저장소 / 스레딩
- Project → Application 레벨 이전. 프로젝트 N개를 열면 번들 파일을 N번
파싱하고 N배 메모리를 상주시켰으며, 사전을 프로젝트마다 재임포트해야
했고 상태 파일이 .idea/ 에 커밋됐다. 1.x 프로젝트 데이터는
postStartupActivity 로 이전한다.
- CanonicalStore.init{} 의 3.7MB EDT 파싱 제거. 툴윈도우 생성자에서
도달하던 경로였다. 지연 로딩 + 백그라운드 빌드.
- 인덱스 소유권을 DictionaryService 로 단일화하고 AtomicReference 로
원자 교체. @volatile null 검사는 원자성이 없어 두 스레드가 각각
전체 인덱스를 만들고 하나를 버렸다.
- ProcessCanceledException 재던지기. catch (Throwable) 이 삼켜서
취소가 동작하지 않았다.
- 누락된 부모 Disposable 로 인한 패널 누수 수정.
- Gson 제거. 선언되지 않은 의존성이었고 IDE 내부 jar 에 우연히 존재할
뿐이라 언제든 깨질 수 있었다. 자체 JSON 파서로 교체하고 영속화는
플랫폼 xmlb 에 맡긴다.
- EntrySource 의 XLSX(1.4.1 에서 제거된 importer 잔재)를 BUNDLED 로 정규화.
## 언어 확장
- com.intellij.java / org.jetbrains.kotlin 하드 의존 제거. WebStorm,
DataGrip, PyCharm Community 가 설치 자체를 거부했다.
- EgovLanguageSupport EP 신설 + Java/Kotlin/SQL/JS·TS 를 optional
config-file 로 분리. SQL 은 이 표준이 원래 DB 컬럼명을 위해 만들어진
만큼 적합도가 높은데 그동안 닿을 수 없었다.
- SQL/JS 는 플랫폼 PsiNameIdentifierOwner 로 구현해 해당 플러그인에
컴파일·런타임 의존이 없다. SQL 은 snake_case 를 강제한다.
## 데이터 파일
- Kotlin lazy 델리게이트가 직렬화된 stableKey$delegate 5,395건 제거,
"-" 플레이스홀더 제거, 최소화. 3.73MB → 1.98MB (47% 감소).
## 빌드
- gradle.properties 에 file.encoding=UTF-8 고정. 한국어 Windows 에서
Gradle 이 MS949 로 README 를 읽어 마켓플레이스 설명이 깨진 채
배포됐다(재현 확인). description 도 asBytes + 명시적 UTF-8 로 읽는다.
- plugin.xml 의 version/description/change-notes 약 82줄 삭제.
patchPluginXml 이 매 빌드 폐기하던 죽은 코드이며 CHANGELOG 와 이미
내용이 갈라져 있었다.
## 테스트
템플릿 잔재(MyPluginTest, testData/rename) 제거. 위 결함들을 회귀로
고정하는 테스트 60개 추가: stableKey 충돌, 크래시 입력 5종, 무매치,
limit 과 랭킹, 타입 필터, 서브토큰/초성/한글 부분일치, 번들 데이터
실측치(7,200 / 7,197), JSON 파서 경계.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AI 코딩 에이전트에게 변수명을 맡기면 표준 사전을 무시하고 식별자를
지어낸다. 플러그인이 이미 갖고 있는 7,200건 사전과 랭킹을 에이전트가
그대로 조회할 수 있도록 노출한다.
- EgovDictionaryRestService: IDE 내장 웹서버(기본 63342)에 읽기 전용
엔드포인트 3개.
GET /api/egovconstant/status
GET /api/egovconstant/search?q=&type=&limit=
GET /api/egovconstant/convert?abbr=
검색 결과에는 공식 약어와 camel/Pascal/snake/CONSTANT 4종 표기를 함께
담는다. DOMAIN 항목은 라틴 표기가 없으므로 표기 필드를 넣지 않는다.
- Origin 헤더가 없는 요청만 바로 처리하고, 있는 요청(사실상 브라우저)은
플랫폼 확인 절차를 따른다. 로컬 도구는 매번 다이얼로그 없이 쓰고,
웹페이지가 몰래 IDE를 조회하지는 못하게 하는 절충이다.
- tools/mcp/egovconstant-mcp.mjs: 위 엔드포인트를 감싸는 MCP 서버.
stdio JSON-RPC, Node 내장 모듈만 사용(의존성 0).
도구 3개: egov_search / egov_convert / egov_status.
랭킹·초성 검색·사용자 임포트 사전이 모두 플러그인에 있으므로 재구현이
아닌 프록시로 두었다. 대신 IDE가 실행 중이어야 한다.
IDE에 닿지 못하면 포트 확인 방법까지 알려주는 오류를 반환한다.
- .mcp.json 으로 이 저장소에서 바로 쓸 수 있게 등록.
- tools/mcp/README.md 에 영/한 사용법, 포트 문제(runIde 샌드박스는 63343),
curl 예시, 트러블슈팅 정리.
검증: 샌드박스 IDE를 띄워 실제로 확인했다.
- status -> {"ready":true,"entries":7197,"customEntries":0}
번들 7,200건이 중복 3건 병합 후 7,197건으로 로드되는 것이 런타임에서
일치.
- 사용자 -> USER(10000) > 사용자명(6021) > 사용자수(5503) 로 길이 정규화 동작
- nm -> 명/NM 1위, 이후 이니셜 매치(NTC_MTTR = ntc+mttr)
- 금액 -> AMT 1위 후 금액N13.. (한글 부분일치)
- ㅅㅇㅈ -> 승인자/수익자/사업장/사업자/신용장/소유자/사용자 (초성 검색)
- zzzzzzzz -> count 0 (무매치가 실제로 0건)
- convert USER_ID_YN -> userIdYn / UserIdYn / user_id_yn / USER_ID_YN
랭킹 수정: 위 실측 중 user_nn(1글자 오타)이 USER_NM 대신 USER_ENG_NM을
1위로 올리는 것을 발견했다. 편집거리 1이 흩어진 서브시퀀스 매치보다
약하게 평가되고 있었다(1380 vs 1720). K_FUZZY 0.18 -> 0.34.
실데이터 7,200건에 대한 랭킹 회귀 테스트를 추가해 고정했다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## 버전/릴리스 자동화
매 릴리스마다 손으로 하던 일을 없앤다. 기존 절차는 gradle.properties 의
pluginVersion 수정 → CHANGELOG 작성 → plugin.xml 의 중복 version 동기화
(이 중복은 EGC_006 에서 제거) → 빌드가 만든 드래프트 릴리스를 찾아 수동
발행이었다.
- scripts/next-version.sh : 마지막 릴리스 이후 커밋에서 bump 레벨을 판정.
[major]/BREAKING CHANGE → major, [minor]/feat: → minor, 그 외 patch.
이 저장소에는 태그가 없었으므로 폴백을 3단으로 뒀다: 태그 → 이전 자동
bump 커밋 → 전체 히스토리. 전체 히스토리로 떨어져도 명시적 마커만
patch 를 넘어가므로 안전하다.
- scripts/prepare-changelog.sh : [Unreleased] 가 비어 있으면 커밋 제목으로
채운다. 손으로 쓴 내용이 있으면 건드리지 않는다. 릴리스 노트가 빈 채로
발행되는 것을 막는 목적이다.
- build.yml : releaseDraft(수동 승인 필요) 를 release 잡으로 교체.
버전 산출 → gradle.properties 반영 → patchChangelog → 커밋·푸시 →
gh release create. test/verify/qodana 통과가 선행 조건이다.
bump 커밋에 [skip ci] 를 넣어 재트리거를 막는다.
- release.yml : CHANGELOG 재패치와 changelog PR 생성 단계 제거. 이제
릴리스 생성 전에 이미 패치되므로 중복이었다.
- main 병합이 곧 마켓플레이스 배포가 된다는 뜻이므로, 커밋 메시지에
[skip release] 로 개별 차단할 수 있게 했다.
- 두 스크립트를 실행 권한과 함께 커밋하고, 양쪽 경로(비어있음/내용 있음,
태그 있음/없음)를 로컬에서 확인했다.
## 문서
- README 재작성. 영/한 병기이며 <!-- Plugin description --> 블록이
마켓플레이스 설명으로 주입되므로 양 언어를 모두 담았다.
깨져 있던 MARKETPLACE_ID 배지 2개와 404 링크 제거(발행 ID 미확정).
1.6.2 이후 갱신되지 않아 존재하지 않는 버튼명("Import JSON",
"Reset to Default")을 안내하던 사용법도 실제 UI에 맞게 수정.
데이터셋 규모 표, 검색 입력 형태 표, 설정 표, 릴리스 규칙 추가.
- CHANGELOG 2.0.0 항목 작성. 사용자가 체감하는 표현으로 쓰고 내부 용어는
쓰지 않았다.
- pluginVersion 2.0.0. 이번 변경은 저장 위치 이전과 하드 의존성 제거가
포함된 major 이므로 명시적으로 지정했고, 이후 bump 는 자동화가 맡는다.
검증: clean buildPlugin + check + verifyPluginStructure +
verifyPluginProjectConfiguration 통과. 패치된 plugin.xml 에 version 2.0.0,
한글 설명, 2.0.0 changeNotes 가 모두 정상 주입됨을 확인. 아티팩트
eGovConstant-2.0.0.zip 생성. 테스트 64개 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
runIde 로 실제 화면을 확인하다 발견한 두 가지를 고친다. - 유형 필터 4개가 텍스트 없이 빈 원으로 그려지던 문제. ActionToolbar 는 아이콘을 그리지 텍스트를 그리지 않는다. displayTextInToolbar() 를 오버라이드해 라벨이 나오게 했다. - 검색어가 비어 있을 때 사전을 키 순으로 나열해 3음절 용어가 무작위처럼 보이던 문제. 랭킹 근거가 없는 상태이므로 최근 사용 항목을 보여주고, 없으면 안내 문구(search.empty.initial)로 떨어지게 했다. 다만 유형 필터가 걸려 있으면 "이 분류를 훑어보겠다"는 의도이므로(도메인은 106건) 그대로 나열한다. 마켓플레이스 배지는 실제 ID 로 복원했다. 등록되어 있지 않은 줄 알고 EGC_008 에서 제거했으나, 확인 결과 이미 게시되어 있다 (id 29497, xmlId com.github.yohanki.egovconstant). 최초 수동 제출 없이 publishPlugin 이 바로 동작한다는 뜻이기도 하다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## 인텐션 중복 등록 runIde 로그에서 발견: SEVERE - 2 intention duplicates found for Rename to a standard name EGC_006 에서 같은 클래스를 egov-java.xml 과 egov-kotlin.xml 양쪽에 <language> 별로 등록한 것이 원인이다. 플랫폼은 이를 중복 인텐션으로 판정하고 SEVERE 를 남기며, 사용자에게는 "IDE error occurred" 알림으로 보인다. 언어별 등록 대신 코어 plugin.xml 에 한 번만 등록하고, 범위 제한은 isAvailable 에서 EgovLanguageSupport 존재 여부로 한다. 이 EP 가 애초에 "이 언어를 지원하는가"를 결정하는 곳이므로 단일 근거가 되고, SQL 과 JavaScript 도 추가 등록 없이 함께 커버된다. <language> 를 생략하는 것 자체는 1.x 와 같지만, 1.x 에는 이런 검사가 없어서 JSON·YAML·Markdown 등 모든 파일에서 Alt+Enter 메뉴에 나타났다. 재검증: 재실행 후 로그에서 intention duplicates 0건. 남은 SEVERE 는 번들 Gradle 플러그인의 GradleJvmSupportMatrix 로 우리 코드와 무관하며 이번 변경 이전에도 발생했다. ## MCP 문서 README 의 해당 절이 tools/mcp/README.md 를 가리키기만 해서 그 자체로는 읽히지 않았다. 동작 방식 다이어그램, .mcp.json 설정 예시, 포트 확인 방법, 도구 3종 표, 실제 요청/응답 예시, MCP 없이 curl 로 쓰는 법, 보안 경계 설명을 영/한 병기로 추가했다. 이 블록은 마켓플레이스 설명으로도 주입된다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Qodana Community for JVM3 new problems were found
💡 Qodana analysis was run in the pull request mode: only the changed files were checked View the detailed Qodana reportTo be able to view the detailed Qodana report, you can either:
To get - name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2025.1.1
with:
upload-result: trueContact Qodana teamContact us at qodana-support@jetbrains.com
|
## 단어 뒤 대문자 조합 (사용자 지적 사항) 사전은 완성된 식별자 목록이 아니라 어휘집이다. HS 는 주택, NM 은 명이지만 HS_NM 항목은 없으므로 hsN 은 조회가 아니라 "hs 를 두고 N 으로 시작하는 단어를 잇는다"로 읽어야 하고 hsNm 을 내놔야 한다. 기존에도 tail 검색은 있었으나 tail 매치와 whole 매치를 구분하지 않고 둘 다 head 에 이어붙였다. 그 결과 userNm 을 입력하면 USER_NM 항목을 찾아놓고 user + UserNm = userUserNm 을 만들었고, IDE prefix matcher 가 조용히 걸러내 정확히 일치하는 항목이 사라졌다. 두 해석을 Mode.APPEND / Mode.REPLACE 로 분리했다. 또 하나: insertText 가 prefix 와 같으면 건너뛰는 가드가 정답을 지우고 있었다. userNm 입력 시 NM(명) 으로 만든 userNm 이 걸러지고 userNtcMttr 같은 조합이 1위로 올라왔다. APPEND 모드에만 적용하도록 바꿔, 그 자리를 whole 매치가 올바른 항목(USER_NM 사용자명)으로 채우게 했다. 로직을 CompletionSearchStrategy 로 분리했다. prefix 산술은 2.0 이전 크래시가 살던 자리인데 테스트가 하나도 없었다. ## 성능 (실측) 인덱스 빌드 54ms / 7,200건, 메모리 9.2MB, 최악 검색 6ms, userAccountNm 13타 전체 28ms. 회귀를 잡는 성능 테스트를 추가했다. 임계값은 CI 러너에서 자릿수 회귀만 잡도록 느슨하게 두고 수치는 항상 출력한다. ## 스펠체커 연동 이 사전으로 만든 이름은 정의상 영어 단어가 아니다(userNm, regYmd, bplcSeCd). 따라서 표준을 지킬수록 노란 물결이 늘어나고, 해결책은 인스펙션을 끄거나 단어를 하나씩 IDE 사전에 넣는 것뿐이었다. DictionaryChecker EP 로 답한다. 단어 목록을 기여하는 대신 질의에 답하므로 사용자가 가져온 항목도 자동으로 함께 인정된다. IDE 는 식별자를 단어 단위로 쪼개 검사하므로 약어의 서브토큰(약 3,000개)을 기준으로 판정한다. 한 글자는 제외했다 - 프로젝트 전역에서 진짜 오타를 덮을 뿐이다. 설정에서 끌 수 있다. ## 내보내기 / 가져오기 충돌 - 팀 공유용 export 추가. 사용자가 가져온 항목만 JSON 으로 저장한다(번들 데이터는 모두 동일하므로 제외). 커밋해서 리뷰할 수 있도록 들여쓴다. - import 시 이미 있는 항목이 있으면 어떻게 할지 묻는다. 기존 값 유지 / 가져온 값으로 덮어쓰기 / 새 항목만 / 취소. 한쪽을 기본으로 가정하지 않는 이유는 둘 다 정당하기 때문이다 - 팀이 합의한 사전은 대개 이겨야 하고, 개인 사전은 대개 아니다. 충돌이 있을 때만 묻는다. - 어느 정책이든 컬렉션은 합집합으로 둔다. 필드를 덮어쓰는 것은 선택이지만 동의어를 버리는 것은 그냥 손실이다. - JSON 오류에 줄/칸 위치를 표시한다. ## Qodana - API marked for removal(Failure): displayTextInToolbar() 오버라이드를 ActionUtil.SHOW_TEXT_IN_TOOLBAR 프레젠테이션 속성으로 교체. - @ApiStatus.Internal intIncrementModificationCount() 사용 3건 제거. 리스트를 제자리 변경하면 컴포넌트가 수정됨으로 표시되지 않으므로, 상태 클래스 안으로 옮겨 protected incrementModificationCount() 를 쓴다. - 미사용 심볼 제거. 다만 toggleFavorite 는 제거 대신 UI 를 붙였다 - 즐겨찾기 필터와 ★ 표시는 있는데 정작 추가할 방법이 없었다. - JsonParseException.offset 도 제거 대신 실제로 썼다(오류 위치 표시). - 가시성 축소, 중복 한정자, when 내부 변수 선언, withIndex, KDoc 링크 오인식, 빈 기본 생성자 정리. - qodana.yml linter 를 CLI 버전(2025.1)에 맞춤. ## 검증 테스트 100개 통과(10개 클래스). buildPlugin / verifyPluginStructure 통과. 참고: 이 커밋 도중 Kotlin 증분 컴파일이 "기본 인자 추가"를 ABI 호환으로 오판해 기존 테스트가 옛 시그니처를 계속 호출하는 현상이 있었다. 코드 문제가 아니며 --no-build-cache --rerun-tasks 로 해소된다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
시니어 4인(검색/알고리즘, 플랫폼/아키텍처, UI 엔지니어링, 비주얼 디자인)이 독립적으로 분석하고, 2명 이상이 같은 결론에 도달한 항목만 확정 결함으로 취급해 재설계했습니다. 단독 보고는 코드나 실행으로 직접 검증한 것만 반영했습니다.
main에 머지되는 순간 build.yml 의 release 잡이 버전 bump → CHANGELOG 패치 → 태그 → GitHub 릴리스를 만들고, 이어서 release.yml 이 마켓플레이스에 게시합니다. 원하지 않으면 머지 커밋 메시지에[skip release]를 넣으세요.데이터 손실 · 크래시 (실측 근거)
stableKey에 type 이 빠져 TERM/WORD 가 약어를 공유하면 충돌(149쌍). 로더가 map 에 덮어써 WORD 가 TERM 의 도메인·포맷 메타데이터까지 가져감. 수정 후 고유키 7,048 → 7,197prefix의 인덱스를 무관한suggestion에 적용. 2자 이하 약어 51건 / 4자 이하 1,208건 때문에val userNm만 타이핑해도 재현. 예외를 피해도 결과 오염(usrI+ipAddr→usrddr)document.replaceString으로 커서 위치 텍스트만 교체 → 선언부만 바뀌고 모든 사용처가 미해결 심볼. 실제 리팩터링으로 교체검색 엔진 (Trie → 역색인)
score > 0필터가 무력화, trie 미스는 전체 스캔 폴백 → 오타 하나가 7,200건 전부 반환_접미어 검색 불능:USER_NM이usernm한 덩어리로 색인 →nm이 972건 중 9건에만 도달. 서브토큰 역색인으로 해결구조
.idea오염). 1.x 데이터는 자동 마이그레이션Settings | Tools로 이전신규
검증
clean buildPlugin/check/verifyPluginStructure/verifyPluginProjectConfiguration통과entries: 7197, 자동완성 크래시 조건에서 정상 동작, 툴윈도우 2탭·4종 표기·필터·빈 상태, 아이콘이#CED0D6(다크 변형)로 렌더 →_dark해석 동작 확인검증하지 못한 것
🤖 Generated with Claude Code