egovframe-scaffold-mcp 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -44,7 +44,7 @@ Requires Node.js 18+. Downloads go to `codeload.github.com`, `raw.githubusercont
44
44
  | `EGOVFRAME_ALLOWED_ROOTS` | Optional sandbox: every directory argument (`projectDir`, `outputDir`) must be inside one of these roots (path-separator-delimited, realpath-checked). |
45
45
  | `EGOVFRAME_LANG` | `en` → English tool descriptions in `tools/list`. Responses stay Korean. |
46
46
 
47
- ## Tools (30)
47
+ ## Tools (31)
48
48
 
49
49
  | Tool | What it does |
50
50
  |---|---|
@@ -68,6 +68,7 @@ Requires Node.js 18+. Downloads go to `codeload.github.com`, `raw.githubusercont
68
68
  | `migrate_egovframe_project` | **5.x migration**: diagnose RTE coordinates, package/class renames and removals (runtime and common components), javax→jakarta, web.xml, removed XML namespaces, libraries to replace (auto/manual with file:line); `apply=true` rewrites auto items in one transaction with backups; `verify=true` compiles and links compiler errors to the remaining manual items as a prioritised worklist |
69
69
  | `check_egovframe_dependencies` | Compare dependencies (declared, or with `resolve=true` the whole tree resolved by Maven/Gradle — transitive artifacts with their path, declared-vs-resolved differences) with the official 5.x parent baseline (139 coordinates + BOM families), the full Spring Boot BOM (1,473 coordinates) and the transitive dependencies of the 18 RTE modules (58): ok / outdated / parent-managed / legacy / replace / vendor / unknown, each with the basis it was compared against; security-config presence checks; optional OSV lookup |
70
70
  | `reassemble_egovframe_components` | Reassemble the common-component sources copied into a 3.x/4.x project onto v5.0.7: identifies the original tag by git blob ids (no downloads), three-way classifies every file, keeps user changes as patches plus a worklist, backs up and removes files 5.x dropped, writes the manifest (then upgrade/validate/remove work); dryRun by default, one transaction, optional compile verify |
71
+ | `rehearse_egovframe_migration` | Rehearse the 5.x migration on a copy without touching the project: reassemble components (first, so the origin tag is still recognisable), apply the automatic items, compile (every javac error, no 100-error cap), align the copy's pom to the official common-components pom of the pinned tag, compile again. Reports both error counts, missing packages with candidate coordinates, cascade errors, remaining files, a worklist and the pom patch; the original is fingerprinted before and after. The official v4.3.2 tree goes from 6,003 errors (JDK 17; 6,335 on JDK 21) to 1 |
71
72
  | `generate_egovframe_sbom` | CycloneDX 1.6 JSON SBOM without touching build files — Maven via cyclonedx-maven-plugin (hashes, licenses), Gradle from the resolved tree; baseline verdict per component (`egovframe:*` properties), optional OSV `vulnerabilities[]`; writes inside the project only, `dryRun` by default |
72
73
  | `check_egovframe_sbom` | Check an existing CycloneDX SBOM without changing it: the 7 minimum elements (supplier, name, version, unique identifier, dependency relationship, author, timestamp) per component with a ready / needs-work verdict, a purl-only re-check (changed baseline verdicts, OSV findings published since generation), a diff against `baselinePath`, and with `vex=true` a CycloneDX VEX draft (`in_triage`, BOM-Link) that keeps human decisions |
73
74
  | `diagnose_egovframe_network` | Probe the hosts the server downloads from, classify failures (DNS, timeout, TLS, proxy auth) and prescribe environment settings as bash/cmd/PowerShell commands |
@@ -80,7 +81,7 @@ Resources: `egovframe://catalog/components`, `…/components/{id}`, `…/templat
80
81
  - **Pinned sources**: component catalog (official v5.0.7 tag, commit, archive sha256), Initializr zip templates (commit, sha256, size), config templates (commit, per-file sha256, CRLF-safe), migration rules derived from `egovframe-runtime` tags v3.10.0 / v4.3.0-Final / v5.0.2-Final and `egovframe-common-components` v3.10.0 / v5.0.7, dependency baseline extracted from the official 5.x parent poms (5.0.2), `spring-boot-dependencies` 3.5.6 and the RTE 5.0.2 module poms — every pom with url and sha256. `sync_egovframe_templates` reports when any of these drifts upstream.
81
82
  - **Release gate**: `npm run prepublishOnly` runs the build and ~28 offline suites (over 1,300 assertions) on ubuntu and windows × Node 18/20/22; an integration job downloads real upstream assets, compiles generated CRUD and a migrated 3.10 project with JDK 17, checks that every migration target coordinate exists in the Maven repositories, watches upstream drift of the bundled rules and baseline, and validates `server.json` against the MCP Registry.
82
83
  - **Automated releases** (v0.35+): once a PR is merged and CI passes on `main`, `release.yml` publishes to npm with OIDC trusted publishing (provenance attached, no tokens), tags the verified commit, creates the GitHub Release from the changelog and publishes to the MCP Registry with GitHub OIDC — only when the version in `package.json`, `server.json` and the changelog agree and the version is not released yet.
83
- - **Protocol metadata**: every tool carries ko/en `title` and MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`); the five diagnostic tools, the SBOM generator and checker, the report tool and the reassembly tool also declare `outputSchema` and return `structuredContent`.
84
+ - **Protocol metadata**: every tool carries ko/en `title` and MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`); the five diagnostic tools, the SBOM generator and checker, the report tool, the reassembly tool and the rehearsal tool also declare `outputSchema` and return `structuredContent`.
84
85
  - **Regression corpus** (v0.37): CI runs diagnosis, dry-run apply, the dependency check and the assessment against the official `egovframe-common-components` **v3.10.0 and v4.3.2** source trees (pinned commits, sparse partial clones) and asserts the counts in `catalog/migration-corpus.json` within ±1% — so a rule or baseline change that moves real-world results is caught, and the 4.x→5.x path is verified on real assets.
85
86
  - **Resumable releases**: `release-check` looks at npm (including the published `gitHead`), the tag, the GitHub Release and the MCP Registry and runs only the missing steps, so an interrupted release continues on the next merge instead of needing manual tags.
86
87
  - **Safety**: transactions with structured rollback reports, `dryRun` everywhere, allowed roots with realpath checks, zip-slip guards, process-tree kill on build timeouts.
@@ -118,7 +119,7 @@ npx -y egovframe-scaffold-mcp sbom --project . --write
118
119
  npx -y egovframe-scaffold-mcp sbom-check --project . --offline=false --fail-on "minimum,newVulns"
119
120
  ```
120
121
 
121
- With no arguments the package starts the MCP stdio server as before; with a command (`assess`, `check`, `sbom`, `sbom-check`, `migrate`, `validate`, `diagnose`, `network`) it runs that one tool through an in-memory MCP client and exits. Options use the tool parameter names (kebab-case allowed); `--json` prints the same object as MCP `structuredContent`; `--step-summary` appends the Markdown to `$GITHUB_STEP_SUMMARY`; `--fail-on` takes grade (`supplyChain:C`) or count (`vulnerabilities`, `manual>20`) conditions. Exit codes: 0 pass, 2 over the `--fail-on` threshold, 3 execution failure, 64 usage error. Write tools are not exposed. `generate_egovframe_ci(supplyChain=true)` adds a GitHub Actions job that builds the SBOM, runs the assessment with the grades in the PR summary, uploads both, and fails above `failOn`. Design notes: [docs/design-cli.md](docs/design-cli.md) (Korean).
122
+ With no arguments the package starts the MCP stdio server as before; with a command (`assess`, `check`, `sbom`, `sbom-check`, `rehearse`, `migrate`, `validate`, `diagnose`, `network`) it runs that one tool through an in-memory MCP client and exits. Options use the tool parameter names (kebab-case allowed); `--json` prints the same object as MCP `structuredContent`; `--step-summary` appends the Markdown to `$GITHUB_STEP_SUMMARY`; `--fail-on` takes grade (`supplyChain:C`) or count (`vulnerabilities`, `manual>20`) conditions. Exit codes: 0 pass, 2 over the `--fail-on` threshold, 3 execution failure, 64 usage error. Write tools are not exposed. `generate_egovframe_ci(supplyChain=true)` adds a GitHub Actions job that builds the SBOM, runs the assessment with the grades in the PR summary, uploads both, and fails above `failOn`. Design notes: [docs/design-cli.md](docs/design-cli.md) (Korean).
122
123
 
123
124
  ## Reassembling copied common components
124
125
 
package/README.md CHANGED
@@ -15,9 +15,9 @@ Claude, VS Code(Copilot), Cursor 등 MCP를 지원하는 AI 도구에서 **대
15
15
 
16
16
  현재 v0.36.1은 **도구 28종(title·annotations·구조화 출력 6종), 공식 템플릿 22종, 설정 템플릿 21종, 5.x 전환 규칙(RTE 18모듈·클래스 331종 + 공통컴포넌트 1,089종 근거)과 적용·검증, 의존성 기준(공식 parent 관리 좌표 139종+계열 6종+Spring Boot BOM 1,473종+RTE 전이 58종)·해석된 의존성 트리·CycloneDX SBOM 과 규칙·기준 drift 감시, 네트워크 진단·AGENTS.md 생성, 공통컴포넌트 카탈로그 190항목(리프 176종+그룹 14종)**을 제공합니다.
17
17
 
18
- ## 진행 현황 (2026-10-07)
18
+ ## 진행 현황 (2026-10-08)
19
19
 
20
- - **소스 기준**: v0.40.0(`package.json`), 도구 30종 + CLI 명령 8종·공식 템플릿 22종·설정 템플릿 21종·카탈로그 190항목(공통컴포넌트 v5.0.7).
20
+ - **소스 기준**: v0.41.0(`package.json`), 도구 31종 + CLI 명령 9종·공식 템플릿 22종·설정 템플릿 21종·카탈로그 190항목(공통컴포넌트 v5.0.7).
21
21
  - **안전성 기준선 완료**: [PR #7](https://github.com/EricSeokgon/egovframe-scaffold-mcp/pull/7)~[#16](https://github.com/EricSeokgon/egovframe-scaffold-mcp/pull/16)을 반영해 provenance·strict assertion·safe remove와 프로젝트 생성/레시피/직접 조립/AI 조립/업그레이드 transaction을 갖췄습니다.
22
22
  - **v0.22.0 추가**: `EGOVFRAME_ALLOWED_ROOTS`를 19개 도구 진입점에 적용해 `..`·symlink/junction 이탈을 차단하고, 실패 시 rollback 결과를 구조화해 반환합니다. [PR #16](https://github.com/EricSeokgon/egovframe-scaffold-mcp/pull/16)은 7파일(+340/−31), 로컬 16종 스위트 전체 통과 후 병합됐습니다.
23
23
  - **v0.23.0 추가**: `build_egovframe_project` — 생성한 프로젝트를 실제로 빌드(maven/gradle·mvnw/gradlew 자동 감지)하고 컴파일·테스트 오류를 파일/라인 단위로 구조화해 생성→검증 루프를 완성합니다. 타임아웃·로그 상한·허용 root·dryRun 포함. [PR #18](https://github.com/EricSeokgon/egovframe-scaffold-mcp/pull/18)은 4파일(+458/−3), 오프라인 40단언과 실제 spawn 경로를 검증했습니다.
@@ -32,6 +32,7 @@ Claude, VS Code(Copilot), Cursor 등 MCP를 지원하는 AI 도구에서 **대
32
32
  - **v0.30.0 추가**: `migrate_egovframe_project` 2단계(`apply=true`) — 진단의 auto 항목을 하나의 transaction 으로 실제 치환합니다(dryRun 기본, 원본 `migration-backup/` 보관, `migration-plan.json`, 실패 시 복구). 3.10 좌표·javax 픽스처를 적용한 뒤 JDK 17 `mvn compile` 통과를 CI 에서 확인합니다. `check_egovframe_dependencies` — 공식 5.x parent 2종에서 추출한 기준(관리 좌표 139종 + BOM 계열 7종, `catalog/dependency-baseline.json`)과 의존성을 대조해 기준 충족/미만/parent 관리/전환 대상/교체 필요/기준 없음으로 분류하고, 보안 설정(sec.security·CSRF·XSS 필터·보안 헤더·HTTPS 저장소) 존재 여부를 근거와 함께 보고합니다. 기본 오프라인, `offline=false` 면 OSV 취약점 조회(설계: [docs/design-dependency-check.md](docs/design-dependency-check.md)).
33
33
  - **v0.31.0 추가**: 운영 편의 — `diagnose_egovframe_network`(도구가 쓰는 호스트 7종에 DNS·HEAD 프로브, 실패 종류 분류, HTTPS_PROXY+NODE_USE_ENV_PROXY·IPv4 우선·사내 CA 처방을 bash/cmd/PowerShell 명령으로), 모든 다운로드 실패 메시지에 같은 처방 한 줄 부착, `generate_agents_md`(진단 결과로 AI 코딩 도구용 AGENTS.md, ko/en), 영문 README(`README.en.md`)와 `EGOVFRAME_LANG=en` 영문 도구 설명, MCP Registry 메타데이터(`server.json`·`mcpName`). 기획 3개 버전(v0.29–v0.31)이 모두 완료됐습니다.
34
34
  - **v0.32.0 추가**: MCP 프로토콜 현대화 — 도구 27종을 `registerTool` 로 전환해 `title`(ko/en)과 annotations(`readOnlyHint` 14종·`destructiveHint` 4종·`idempotentHint`·`openWorldHint`)를 `tools/list` 에 노출하고, `format=json` 을 제공하던 5종(`diagnose_egovframe_project`·`validate_egovframe_project`·`migrate_egovframe_project`·`check_egovframe_dependencies`·`diagnose_egovframe_network`)은 `outputSchema` 와 `structuredContent` 를 함께 돌려줍니다(기존 `text` 유지). 테스트 이식성 가드(`scripts/check-test-portability.mjs`, 게이트 포함)와 플랫폼 주입 단언으로 Windows 전용 실패 재발을 막습니다.
35
+ - **v0.41.0 추가**: 전환 리허설 — 새 도구 **`rehearse_egovframe_migration`**(31번째)이 프로젝트를 건드리지 않고 사본에서 공통컴포넌트 재조립 → 전환 자동 항목 적용 → 컴파일 → pom 을 공식 공통컴포넌트 v5.0.7 기준으로 맞춤 → 다시 컴파일까지 돌려 "자동 단계 뒤 사람이 고칠 것이 실제로 몇 건인가"를 잽니다(javac 의 오류 100개 상한 없이 전부 셈, 실행 전후 지문으로 원본 불변 확인). 공식 공통컴포넌트 **v4.3.2 전체 트리**(6,523 파일)는 자동 단계 뒤 컴파일 오류 **6,003건**(CI·JDK 17, JDK 21 은 6,335건) 중 4건을 뺀 전부가 누락 패키지와 그 연쇄(pom 의존성, 그 때문에 Lombok 이 돌지 못한 getter 등)였고, pom 을 맞추면 **1건**(GPKI 벤더 jar 의 javax 참조)만 남습니다 — CI 가 이 값을 기대값으로 고정합니다. 실측 중 찾은 두 결함도 고쳤습니다: 재조립은 전환 적용보다 **먼저** 해야 원본 태그가 식별되고(반대로 하면 4.3.2 가 v5.0.1 로 오인), 4.x 의 `org.egovframe.rte` 좌표가 5.x 로 분류되던 문제와 Maven 경고·fork 형식이 컴파일 오류로 잘못 읽히던 문제를 바로잡았습니다. 평가서 6절에 최근 리허설 실측이 함께 나옵니다.
35
36
  - **v0.40.0 추가**: SBOM 운영 — 새 도구 **`check_egovframe_sbom`**(30번째)이 이미 만든 CycloneDX SBOM 을 제출물로서 점검합니다. (1) 최소 요소 7종(공급자·구성요소명·버전·고유식별자·의존관계·작성자·생성 시각, NTIA 최소 요소 = 국내 SW 공급망 보안 가이드라인 핵심 구성요소)을 component 마다 세어 "제출 가능/보완 필요"를, (2) 빌드 도구 없이 purl 만으로 기준 판정과 OSV 를 다시 해 생성 이후 바뀐 판정·새로 알려진 취약점을, (3) 이전 SBOM 과의 추가·제거·버전·판정·취약점 차이를, (4) `vex=true` 면 CycloneDX VEX 초안(새 취약점은 `in_triage`, 사람이 적은 판단은 보존)을 냅니다. `generate_egovframe_sbom` 은 `supplier`·`author` 옵션(기본 pom `<organization>`)과 공급자 표로 최소 요소를 생성 단계에서 채웁니다. 공식 web 템플릿 SBOM 은 공급자(주 component 포함 18종)·작성자가 비어 "보완 필요", 옵션으로 다시 만들면 "제출 가능"이고, SBOM·VEX 모두 CycloneDX 1.6 공식 스키마를 통과합니다. CLI `sbom-check`, 평가서 5절에 최소 요소·VEX 상태. 기획 세 버전(v0.38–v0.40)이 모두 완료됐습니다.
36
37
  - **v0.39.0 추가**: CLI 모드와 CI 공급망 게이트 — `npx egovframe-scaffold-mcp <assess|check|sbom|migrate|validate|diagnose|network>` 가 AI 클라이언트 없이 같은 분석을 실행하고 종료합니다(인자 없는 실행은 기존 stdio 서버). `--json` 은 MCP `structuredContent` 와 같은 객체이고, `--fail-on supplyChain:C`·`vulnerabilities`·`manual>20` 같은 기준을 넘으면 종료 코드 2 로 파이프라인을 멈춥니다. `generate_egovframe_ci(supplyChain=true)` 는 SBOM 생성 → 평가서(등급을 PR 요약에) → 아티팩트로 이어지는 게이트 job 을 만듭니다(생성 결과는 CI 에서 actionlint 로 검사). MCP SDK 1.32.1(프로토콜 2025-11-25)로 올렸습니다.
37
38
  - **v0.38.0 추가**: 공통컴포넌트 재조립 실행 — 새 도구 **`reassemble_egovframe_components`**(29번째)가 3.x/4.x 프로젝트에 복사된 공통컴포넌트 소스의 원본 태그를 공식 저장소 태그와 git blob id 로 대조해 찾고(파일 내용은 내려받지 않음), 원본·현재·목표로 파일마다 3-way 판정한 뒤 v5.0.7 로 다시 조립합니다. 사용자 수정은 원본 대비 unified diff 패치로 보존하고 작업 목록으로 돌려주며, 매니페스트를 남겨 이후 `upgrade`·`validate`·`remove` 가 그대로 적용됩니다. 공통컴포넌트 카탈로그·전환 규칙의 대응표를 upstream 새 태그 **v5.0.7**(2026-10-06)로 올렸습니다. 공식 v3.10.0 트리의 `cmm`·`bbs` 를 고친 픽스처에서 원본 v3.10.0 식별(99%), 780개 파일이 v5.0.7 tree 와 일치, `validate` 누락 0·`upgrade` 변경 0 을 CI 에서 확인합니다.
@@ -40,7 +41,7 @@ Claude, VS Code(Copilot), Cursor 등 MCP를 지원하는 AI 도구에서 **대
40
41
  - **v0.35.0 추가**: 릴리스 자동화 — `main` 에서 CI 가 성공하면 `release.yml` 이 네 조건(`server.json` 버전 일치·변경 이력 항목·태그 없음·npm 미배포)을 검사해 npm(OIDC trusted publishing, provenance 자동) → 그 커밋에 태그 → GitHub Release(변경 이력에서 추출) → MCP Registry(GitHub OIDC) 순으로 게시합니다. 사람은 PR 병합만 합니다. 게이트에 `test:release`(판정 함수·릴리스 노트·`npm pack` 내용·크기 상한)와 `server.json` 설명 100자 제한(레지스트리 검증 조건 — 실제 `mcp-publisher validate` 로 발견한 결함 수정)을 추가했습니다.
41
42
  - **v0.34.0 추가**: 의존성 기준 완성 + 규칙·기준 drift 감시 — `check_egovframe_dependencies` 의 기준에 **Spring Boot BOM 전체**(`spring-boot-dependencies` 3.5.6 직접 항목 + BOM import 44종을 한 단계 풀어 1,473 좌표)와 **RTE 모듈 18종의 전이 의존성**(58 좌표, 어느 모듈이 끌어오는지)을 더해 항목마다 기준 출처(parent 직접·계열·Boot BOM·RTE 전이·전환 규칙)를 표시하고, 국내 벤더·기관 배포 좌표는 `vendor` 로, EOL·이전 좌표(옛 MySQL/Oracle 드라이버·Jackson 1·xmlbeans·Ehcache 2·HttpClient 4·ANTLR 3·Spring Social)는 교체 규칙으로 분류해 공식 공통컴포넌트 3.10 pom 의 '기준 없음'을 17 → 1 로 줄였습니다. `sync_egovframe_templates` 가 동봉 규칙·기준의 upstream drift(`egovframe-runtime`·공통컴포넌트 새 태그, 공식 parent 새 버전, 고정 pom sha256 변화)를 갱신 절차와 함께 보고합니다. 기준 parent 를 5.0.2 로 올렸습니다.
42
43
  - **v0.33.0 추가**: `migrate_egovframe_project` 3단계 — `verify=true` 가 compile 을 실행해 컴파일 오류를 수동 항목과 연결하고 "처리하면 해결될 오류 수" 순 작업 목록을 냅니다(javac `symbol:`/`location:` 파싱). 규칙 카탈로그(schemaVersion 2)에 공통컴포넌트 3.x→5.x 대응표(`egovframe-common-components` v3.10.0↔v5.0.6, 제거 54·이동 4)를 추가해 `egovframework.com.*` 제거·이동 클래스를 진단하고, 3.x 공통컴포넌트 소스가 섞인 프로젝트에는 컴포넌트 단위 재조립 권고(`skipComponents` 로 치환 제외)를 냅니다.
43
- - **다음 계획**: v0.38–v0.40 기획을 마쳤습니다. 다음 세 버전 기획은 「다음 기획 진행」 때 이 자리에 정리합니다([지난 기획](#다음-버전-기획-v038v040)).
44
+ - **다음 계획**: v0.42 폐쇄망 오프라인 번들(OSV 덤프·아카이브·템플릿) → v0.43 Jenkins·GitLab CI 와 SPDX. [다음 버전 기획](#다음-버전-기획-v041v043) 참조.
44
45
  - **배포 상태**: npm 최신 배포 버전은 상단 npm 배지와 [npm 패키지 페이지](https://www.npmjs.com/package/egovframe-scaffold-mcp)를 단일 출처로 확인합니다. 이 문서의 버전 표기는 저장소 소스(`package.json`) 기준이며, git 태그 `vX.Y.Z`가 해당 배포본의 커밋을 가리킵니다.
45
46
 
46
47
  ## 제공 도구
@@ -63,6 +64,7 @@ Claude, VS Code(Copilot), Cursor 등 MCP를 지원하는 AI 도구에서 **대
63
64
  | `search_egovframe_docs` | 공식 가이드 문서(egovframe-docs) 인덱스를 키워드로 검색 — 제목·경로·연계 컴포넌트, 문서 URL·조립용 id 반환 (오프라인) |
64
65
  | `generate_egovframe_report` | 프로젝트 리포트 — `sections=["components"]`(기본) 설치 컴포넌트·참조 테이블·가이드 링크·이슈, `sections=["assessment"]` 5.x 전환 준비도 평가서(개요·전환 범위·의존성 조치·보안·SBOM·A–D 등급과 산식). Markdown/json, `outputPath` 로 새 파일 저장(선택) |
65
66
  | `reassemble_egovframe_components` | 3.x/4.x 프로젝트에 복사된 공통컴포넌트 소스를 v5.0.7 로 재조립 — 원본 태그 식별(git blob id 대조), 원본·현재·목표 3-way 판정, 사용자 수정은 패치·작업 목록으로 보존, 5.x 에서 없어진 파일은 백업 후 삭제, 매니페스트 생성, dryRun 기본·transaction·verify(compile) |
67
+ | `rehearse_egovframe_migration` | **전환 리허설** — 프로젝트 사본에서 재조립 → 전환 적용 → 컴파일 → pom 맞춤(공식 공통컴포넌트 v5.0.7 기준 parent·좌표·버전·scope) → 컴파일. 두 시점의 오류 수(javac 상한 없이), 누락 패키지·후보 좌표·연쇄 오류, 남는 수동 항목·파일, 사라지는 오류 순 작업 목록, 사본 pom 패치. 원본 불변(지문 비교), 최근 결과는 평가서에 실측으로 |
66
68
  | `upgrade_egovframe_project` | 설치 컴포넌트를 upstream과 3-way 비교해 갱신 — 사용자 수정 보존, dryRun 기본, 적용 직전 재검증, 파일·백업·매니페스트 단일 transaction (파괴적, 게이트) |
67
69
  | `explain_egovframe_component` | 컴포넌트 하나의 상세(설명·직접/전이 의존성·역의존·참조 테이블·가이드 링크·설치 명령)를 한 번에 반환 (읽기 전용) |
68
70
  | `generate_egovframe_ci` | GitHub Actions CI 워크플로(빌드·테스트) 생성 — maven/gradle 자동 감지, dryRun, 기존 파일 보호. `supplyChain=true` 면 SBOM·전환 준비도 평가서 게이트 job(PR 요약에 등급, `failOn` 기준 초과 시 실패) 추가 |
@@ -269,10 +271,20 @@ generate_egovframe_report(projectDir="/work/legacy-app", sections=["assessment"]
269
271
 
270
272
  파일마다 판정은 목표와 같음(유지)·원본 그대로(교체)·사용자 수정(소스는 교체 + 원본 대비 패치 보존, 메시지·설정 조각·웹 자산은 사용자본 유지 + 목표본 참고 저장)·5.x 신규(추가)·5.x 에서 제거(백업 후 삭제, 사용자 수정이면 패치도)·원본 미확인(백업 후 교체)·사용자 추가(유지)입니다. 백업·패치·`reassemble-plan.json` 은 `migration-backup/<시각>-reassemble-*/` 에, 매니페스트는 `.egovframe-components.json` 에 남습니다. 원본 태그 비교에는 git 이 필요하고, blob 없는 bare 미러를 `EGOVFRAME_CACHE_DIR`(기본 `~/.cache/egovframe-scaffold-mcp`)에 태그당 수백 KB 로 캐시합니다. 사용자 패치를 자동으로 다시 적용하지는 않습니다(5.x 소스가 많이 바뀌어 fuzz 적용이 오히려 위험).
271
273
 
272
- 3.x 프로젝트 전환 흐름:
274
+ ### rehearse_egovframe_migration 파라미터 (전환 리허설)
275
+
276
+ - `projectDir` — 대상 프로젝트(읽기만 함, 허용 root 적용)
277
+ - `steps` — 기본 `["reassemble","migrate","verify","align-pom"]`(순서 고정, 일부만 고를 수 있음)
278
+ - `components`·`sourceTag` — 재조립 단계에 그대로 전달
279
+ - `keepWorkspace` — `true` 면 사본(`EGOVFRAME_CACHE_DIR/rehearsal/<이름>-*`)을 남기고 경로를 돌려줌(기본: 끝나면 삭제), `timeoutMs` — 컴파일 1회(기본 900000), `topN` — 작업 목록 수
280
+
281
+ 사본은 빌드 산출물·VCS·백업 디렉터리를 빼고 만듭니다. 재조립을 전환 적용보다 먼저 하는 이유는 적용이 컴포넌트 소스를 바꾸면 원본 태그 지문이 깨지기 때문입니다(공식 4.3.2 트리를 거꾸로 돌리면 v5.0.1 로 오인되고 1,489개 파일이 "사용자 수정"이 됩니다). Maven 은 `-Dmaven.compiler.fork=true` 와 `JDK_JAVAC_OPTIONS=-Xmaxerrs 100000` 으로 javac 의 오류 100개 상한 없이 셉니다. pom 맞춤은 사본에만 적용하고 패치로 돌려주므로, 검토해서 실제 pom 에 반영하면 됩니다. 실측(공식 공통컴포넌트 v4.3.2 전체): 자동 단계 후 6,003건(JDK 17)·6,335건(JDK 21) → pom 맞춤 후 1건. javac 판본마다 같은 원인을 다른 모양으로 보고하므로(JDK 17 은 없는 패키지의 import 대신 쓰는 곳마다 "cannot find symbol") 분석은 파일의 import 문·타입 선언 파일·Lombok 사용 여부로 원인을 묶습니다. pom 에 소스 인코딩이 없으면 UTF-8 로 컴파일합니다(JDK 18+ 기본값).
282
+
283
+ 3.x/4.x 프로젝트 전환 흐름:
273
284
 
274
285
  ```text
275
286
  generate_egovframe_report(projectDir, sections=["assessment"]) # 평가서: 재조립 권고·수동 항목·등급
287
+ rehearse_egovframe_migration(projectDir) # 사본에서 전 과정 실측: 남는 컴파일 오류·pom 변경안(원본 불변)
276
288
  reassemble_egovframe_components(projectDir) # 미리보기: 원본 태그·파일별 판정
277
289
  reassemble_egovframe_components(projectDir, dryRun=false) # 공통컴포넌트 v5.0.7 재조립 + 패치·작업 목록
278
290
  migrate_egovframe_project(projectDir, apply=true, dryRun=false) # 나머지 좌표·패키지·Jakarta 자동 치환
@@ -364,6 +376,7 @@ npx -y egovframe-scaffold-mcp check --project . --resolve --json --fail-on "vuln
364
376
  npx -y egovframe-scaffold-mcp sbom --project . --write --offline=false
365
377
  npx -y egovframe-scaffold-mcp sbom-check --project . --offline=false --fail-on "minimum,newVulns"
366
378
  npx -y egovframe-scaffold-mcp migrate --project . --fail-on manual
379
+ npx -y egovframe-scaffold-mcp rehearse --project . --fail-on "errors>50"
367
380
  ```
368
381
 
369
382
  | 명령 | 도구 | 비고 |
@@ -371,6 +384,7 @@ npx -y egovframe-scaffold-mcp migrate --project . --fail-on manual
371
384
  | `assess` | `generate_egovframe_report(sections=["assessment"])` | 등급 지표 `migration`·`supplyChain`(예: `supplyChain:C` = C 이상이면 실패) |
372
385
  | `check` | `check_egovframe_dependencies` | `outdated`·`legacy`·`replace`·`unknown`·`vulnerabilities`·`securityMissing` |
373
386
  | `sbom` | `generate_egovframe_sbom` | `--write` 가 있어야 파일을 씀(기본 계획만) |
387
+ | `rehearse` | `rehearse_egovframe_migration` | 사본에서 실측(원본 불변), 지표 `errors`(최종)·`automation`·`aligned`·`files`·`unlinked`·`failedSteps` |
374
388
  | `sbom-check` | `check_egovframe_sbom` | `minimum`(보완 필요면 1)·`gaps`·`newVulns`·`changed`·비교 `added`·`removed`·`versionChanged`·`diffVulns`·VEX `triage`, `--write` 면 VEX 작성·갱신 |
375
389
  | `migrate` | `migrate_egovframe_project` | 진단만(`--apply` 차단), `manual`·`auto`·`items` |
376
390
  | `validate` · `diagnose` · `network` | 같은 이름의 도구 | `invalid`·`missing` · `issues` · `failed` |
@@ -444,8 +458,9 @@ curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.Eri
444
458
  - 네트워크 진단 레벨: 오류 분류 8종, 프로브·DNS 주입으로 7가지 시나리오(전부 도달, 405/404/407/503, 프록시+타임아웃, 프록시 없음+IPv6 혼재, TLS, DNS, 필터)의 처방·안내·자격 증명 가림·셸별 명령을 단언하고, `fetchWithTimeout` 실패 메시지에 처방이 붙는지 확인 (`npm run test:network`, 33단언, 네트워크 불필요). 실제 호스트 7종 프로브는 CI 통합 job (`npm run test:network-live`)
445
459
  - AGENTS.md 레벨: 5.x(매니페스트·래퍼·컴포넌트 2종·백업 디렉터리)와 3.x gradle 픽스처, 빈 디렉터리에서 사실 수집·ko/en 렌더링·dryRun 무기록·기존 파일 거부·overwrite·파일명 검증·staging 정리 (`npm run test:agents-md`, 22단언, 네트워크 불필요)
446
460
  - 레지스트리 메타데이터 레벨: `server.json` ↔ `package.json` 의 이름(`mcpName`)·버전·npm 좌표·스키마 URL·환경변수 문서화 정합 (`npm run test:registry`, 네트워크 불필요)
447
- - 프로토콜 레벨(추가): `EGOVFRAME_LANG=en` 으로 띄운 서버의 도구 30종 설명이 모두 영문이고 표에 빠진 도구가 없는지, `tools/list` 에 `title`·annotations(readOnly 13종·destructive 5종)·`outputSchema` 9종이 노출되는지 확인 (`npm run test:handshake`)
461
+ - 프로토콜 레벨(추가): `EGOVFRAME_LANG=en` 으로 띄운 서버의 도구 31종 설명이 모두 영문이고 표에 빠진 도구가 없는지, `tools/list` 에 `title`·annotations(readOnly 13종·destructive 5종)·`outputSchema` 10종이 노출되는지 확인 (`npm run test:handshake`)
448
462
  - SBOM·해석 레벨: Maven·Gradle 트리 파서(실제 출력 픽스처), `resolve=true` 의 전이 항목·경로·선언/해석 차이·실패 경로, CycloneDX 문서 구성·보강·취약점 병합·출력 경로 거부·overwrite·dryRun (`npm run test:dependencies`, `npm run test:sbom`, 네트워크 불필요); CI 통합이 공식 `egovframe-web` 템플릿을 실제 Maven 으로 해석하고 SBOM 을 생성 (`npm run test:sbom-live`)
463
+ - 전환 리허설 레벨: pom 맞춤(parent 추가·교체, 누락 좌표, 버전·scope, parent 관리 속성, 주석·dependencyManagement·plugin 제외, 멱등), 누락 패키지·연쇄·수동 항목 분류와 후보 좌표, Maven fork 형식·경고 제외 파서, 사본 제외 규칙·원본 지문 불변, 단계 순서(재조립 → 적용 → 컴파일 → pom 맞춤 → 컴파일)·javac 상한 해제 인자, 단계 실패 계속, 기록과 평가서 6절 (`npm run test:rehearse`, 38단언, 네트워크 불필요); CI 통합이 공식 공통컴포넌트 v4.3.2 전체 트리를 실제로 리허설해 재조립·적용 수와 오류 6,003(±10%, 누락 패키지·연쇄 95% 이상)→≤5 를 단언 (`npm run test:rehearse-live`)
449
464
  - SBOM 운영 레벨: 실제 플러그인 출력 고정물(공식 web 템플릿 67 component)로 최소 요소 7종(빠진 항목별)·공급자 표 보완·purl 재판정·가짜 OSV(새/사라진 ID)·비교(추가·제거·버전·판정·취약점)·VEX 생성/판단 보존/같은 ID 의 새 영향 component·깨진 VEX 중단을 단언하고, 생성 SBOM·VEX 를 CycloneDX 1.6 공식 JSON 스키마(specification 1.6.1)로 검증 (`npm run test:sbom-check`, 54단언, 네트워크 불필요); CI 통합이 실제 SBOM 으로 보완 필요(주 component 공급자·작성자) → supplier·author 로 보완(환경에 따른 다른 공백은 `info:` 로그), 두 SBOM 비교 차이 0, 실제 OSV VEX 초안·판단 보존을 확인 (`npm run test:sbom-live`)
450
465
  - 메타데이터·구조화 출력 레벨: `TOOL_META` ↔ 등록 도구 일치, 읽기 전용·파괴·네트워크 힌트 배정, 영문 title, 7종 도구의 실제 결과(진단·검증·전환 진단/적용·의존성·네트워크·리포트, 빈 프로젝트 포함)가 `outputSchema` 를 통과하고 최상위 키가 전부 선언돼 있으며 잘못된 값은 거부 (`npm run test:output-schemas`, 47단언, 네트워크 불필요). MCP 프로토콜 경유 호출 시 SDK 가 `structuredContent` 를 스키마로 검증합니다
451
466
  - 테스트 이식성 레벨: `scripts/check-test-portability.mjs` 가 `test/*.mjs` 에서 Windows 에서 깨지는 가정(정규화 없는 `path.relative` 비교, `./mvnw` 리터럴 기대값, POSIX 절대 경로)을 찾아 실패시킵니다(`npm run check:portability`, 게이트 포함, 의도된 줄은 `// portability: ok <사유>`). 플랫폼 분기 함수(`resolveCommand`·`collectAgentsFacts`)는 `platform` 주입으로 linux·win32 양쪽을 단언합니다
@@ -475,7 +490,7 @@ curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.Eri
475
490
 
476
491
  v0.31.0까지 프로젝트·CRUD 생성, 검증된 공통컴포넌트 실행 자산 조립, 안전성 기반(테스트 판정 강제·사용자 파일 보호·전 도구 트랜잭션·허용 root·구조화 rollback 보고), 생성→검증 루프(실제 빌드·오류 구조화·테스트 리포트 구조화), 공식 템플릿 카탈로그 단일화와 커버리지 확대(22종 중 21종), 설정 파일 생성, 5.x 전환(진단·적용), 의존성 점검, 운영 편의(네트워크 진단·AGENTS.md·영문 설명·레지스트리 메타데이터)를 완료했습니다.
477
492
 
478
- v0.32–v0.34 로 MCP 프로토콜 현대화, 5.x 전환 3단계(검증)와 공통컴포넌트 대응표, 의존성 기준 완성과 규칙·기준 drift 감시를, v0.35–v0.37 로 릴리스 자동화, 해석된 의존성 트리와 SBOM, 전환 준비도 평가서와 회귀 코퍼스를 마쳤습니다(기획 원문과 결과: [이전 기획](#이전-기획-v035v037-완료)). 다음 세 버전(v0.38–v0.40)의 범위는 [다음 버전 기획](#다음-버전-기획-v038v040)에 있습니다. Homebrew 탭은 후순위에서 내렸습니다(npx 로 충분).
493
+ v0.32–v0.34 로 MCP 프로토콜 현대화, 5.x 전환 3단계(검증)와 공통컴포넌트 대응표, 의존성 기준 완성과 규칙·기준 drift 감시를, v0.35–v0.37 로 릴리스 자동화, 해석된 의존성 트리와 SBOM, 전환 준비도 평가서와 회귀 코퍼스를, v0.38–v0.40 으로 공통컴포넌트 재조립 실행, CLI 모드와 CI 공급망 게이트, SBOM 운영(최소 요소·비교·VEX)을 마쳤습니다(기획 원문과 결과: [v0.38–v0.40](#이전-기획-v038v040-완료) · [v0.35–v0.37](#이전-기획-v035v037-완료)). 다음 세 버전(v0.41–v0.43)의 범위는 [다음 버전 기획](#다음-버전-기획-v041v043)에 있습니다. Homebrew 탭은 후순위에서 내렸습니다(npx 로 충분).
479
494
 
480
495
  | 버전 | 핵심 기능 | 목표 |
481
496
  |---|---|---|
@@ -497,13 +512,85 @@ v0.32–v0.34 로 MCP 프로토콜 현대화, 5.x 전환 3단계(검증)와 공
497
512
  | **v0.35 완료** | 릴리스 자동화 (배포 공급망) | `release.yml`: `main` 의 CI 성공 → 네 조건 검사 → npm(OIDC trusted publishing, provenance) → 그 커밋에 태그 → GitHub Release(변경 이력 추출) → MCP Registry(OIDC). `test:release`(판정·노트·tarball 내용·크기), `server.json` 설명 100자 제한, CI 통합의 `mcp-publisher validate` |
498
513
  | **v0.36 완료** | 해석된 의존성 트리 + SBOM | `check_egovframe_dependencies(resolve=true)`: Maven `dependency:tree`·Gradle `dependencies` 로 전이 의존성까지 판정(`origin`·`via`·`differs`), 새 도구 `generate_egovframe_sbom`(28종): CycloneDX 1.6 JSON(Maven 플러그인 / Gradle 트리 구성) + 기준 판정 속성 + OSV `vulnerabilities[]`, 공식 web 템플릿 해석 65 artifact·SBOM 67 component·OSV 24건 |
499
514
  | **v0.37 완료** | 전환 준비도 평가서 + 회귀 코퍼스 | `generate_egovframe_report(sections=["assessment"])`: 개요·전환 범위·의존성 조치·보안·SBOM·A–D 등급(산식 인쇄, 재계산 테스트), json·outputPath. `test:migrate-corpus`: 공식 공통컴포넌트 v3.10.0·v4.3.2 부분 트리 기대값 고정(±1%, CI), 4.x→5.x 실자산 첫 확인. 릴리스 워크플로 재개(npm gitHead 태그) |
500
- | **v0.38 완료** | 공통컴포넌트 재조립 실행 | 평가서의 "재조립 권고"를 실행하는 `reassemble_egovframe_components`: 3.x/4.x 소스의 원본 태그를 지문으로 찾아 3-way 비교(원본·현재·5.0.6) → 5.0.6 조립 + 사용자 수정은 패치·작업 목록으로 보존, 매니페스트 생성(이후 upgrade·validate 적용), dryRun·transaction·compile 검증 |
515
+ | **v0.38 완료** | 공통컴포넌트 재조립 실행 | 평가서의 "재조립 권고"를 실행하는 `reassemble_egovframe_components`: 3.x/4.x 소스의 원본 태그를 지문으로 찾아 3-way 비교(원본·현재·5.0.7) → 공통컴포넌트 최신 v5.0.7(`7912e13`) 로 조립 + 사용자 수정은 패치·작업 목록으로 보존, 매니페스트 생성(이후 upgrade·validate 적용), dryRun·transaction·compile 검증 |
501
516
  | **v0.39 완료** | CLI 모드 + CI 공급망 게이트 | `npx egovframe-scaffold-mcp <assess\|check\|sbom\|migrate\|validate>` — AI 클라이언트 없이 CI·배치에서 같은 분석을 실행(JSON/Markdown, `--fail-on` 등급·판정 임계값, 종료 코드), `generate_egovframe_ci` 의 공급망 게이트 단계(평가서·SBOM 아티팩트), SDK 1.32(프로토콜 2025-11-25) |
502
517
  | **v0.40 완료** | SBOM 운영: 최소 요소·비교·VEX | `check_egovframe_sbom`: 기존 SBOM 의 최소 요소(공급자·구성요소명·버전·고유식별자·의존관계·작성자·생성시각) 충족 점검, 빌드 도구 없이 purl 로 OSV 재조회, 이전 SBOM 과 비교(추가·제거·버전 변경·새 취약점), CycloneDX VEX 초안 생성; `generate_egovframe_sbom` 에 공급자·작성자 메타데이터 — 2027년 공공 SBOM 제출 제도화 대비 |
518
+ | **v0.41 완료** | 전환 리허설 | `rehearse_egovframe_migration`: 프로젝트 사본에서 migrate apply → reassemble → JDK 17 compile 을 돌려 남는 컴파일 오류 수·분포와 "처리하면 오류가 가장 많이 줄어드는 작업" 목록을 실측(원본 불변), 평가서 등급 옆 실측 표시, CLI `rehearse` — 공식 공통컴포넌트 v4.3.2 전체 트리 기대값 고정 |
519
+ | **v0.42 계획** | 폐쇄망 운영: 오프라인 번들 | `prepare_egovframe_offline_bundle`: OSV Maven 덤프·고정 컴포넌트 아카이브·Initializr zip·원본 태그 미러 + sha256 manifest, `EGOVFRAME_OFFLINE_BUNDLE` 로 모든 다운로드·OSV 조회를 번들로 대체(스냅숏 기준일 표시·오래됨 경고) |
520
+ | **v0.43 계획** | CI·제출 형식 다변화 | `generate_egovframe_ci(platform=gitlab\|jenkins)` 와 게이트에 `sbom-check`, SBOM SPDX 2.3 JSON 출력·점검(공식 스키마 검증) |
521
+
522
+ ## 다음 버전 기획 (v0.41–v0.43)
523
+
524
+ v0.40.0 까지 끝난 상태(2026-10-07, 도구 30종 + CLI 명령 8종, 테스트 약 1,600건, npm 0.40.0 자동 배포)에서 다음 세 릴리스를 아래 순서로 진행합니다. 공통 원칙은 이전과 같습니다 — 규칙은 데이터로, 근거는 공식 저장소에서, 쓰기 도구는 dryRun·transaction, 각 버전은 "완료 정의"를 만족해야 릴리스합니다. 이번 세 버전의 공통 주제는 **현장에서 그대로 쓰기** 입니다 — 자동 단계를 모두 돌린 뒤 사람이 고칠 것이 실제로 얼마나 남는지를 사본에서 재고(v0.41), 망분리된 공공 개발 환경에서도 인터넷 없이 같은 결과를 내며(v0.42), GitHub Actions 와 CycloneDX 밖의 빌드 서버·제출 형식(Jenkins·GitLab, SPDX)으로 넓힙니다(v0.43). 선행 조사 근거는 맨 아래 "선행 조사 결과 (2026-10-07)"에 있습니다.
525
+
526
+ ### v0.41.0 — 전환 리허설 (`rehearse_egovframe_migration`)
527
+
528
+ **목표**: 평가서(v0.37)는 전환 난이도를 등급으로, `migrate_egovframe_project`(apply)와 `reassemble_egovframe_components` 는 각 단계의 실행을 맡지만, "자동 단계를 전부 돌린 뒤 컴파일 오류가 몇 개 남고, 무엇부터 고치면 가장 많이 줄어드는가"는 아직 실제로 재 보지 않습니다. 사용자 프로젝트를 건드리지 않고 사본에서 전 과정을 돌려 남는 작업을 실측합니다.
529
+
530
+ **범위 (포함)**
531
+ - 새 도구 `rehearse_egovframe_migration(projectDir, steps=["migrate","reassemble","verify"], components?, sourceTag="auto", keepWorkspace=false, timeoutMs)`(31번째, 프로젝트는 읽기 전용): (1) 사본 작성 — 캐시 디렉터리(`EGOVFRAME_CACHE_DIR/rehearsal/<시각>`)에 복사(`.git`·`target`·`build`·`node_modules`·`migration-backup` 제외, 원본 파일 수·바이트 기록) (2) 사본에 `migrate apply` → `reassemble`(dryRun=false) → JDK 17 compile (3) 결과 — 단계별 변경 파일 수, 컴파일 성공 여부, 남은 오류 수와 파일·패키지·오류 종류별 분포, 오류 ↔ 수동 항목·재조립 작업 목록 연결(v0.33 연결 로직 재사용), "이 작업을 처리하면 오류 N개가 사라진다" 순 상위 작업 목록. 단계 실패는 그 지점까지의 결과와 이유로 돌려줍니다.
532
+ - `keepWorkspace=true` 면 사본 경로를 돌려 사람이 열어 볼 수 있게 하고, 기본은 끝나면 지웁니다. 원본은 실행 전후 디렉터리 지문(경로·크기·mtime)이 같음을 결과에 적습니다.
533
+ - 평가서 6절: 프로젝트에 리허설 결과(`--out` 로 남긴 `rehearsal.json`, 또는 `generate_egovframe_report(rehearsal=true)` 로 즉석 실행)가 있으면 등급 옆에 "실측: 자동 전환 후 컴파일 오류 N건"을 함께 표시합니다(등급 산식은 바꾸지 않음).
534
+ - CLI `rehearse`(지표 `errors`·`files`·`unlinked`, 예: `--fail-on errors>200`).
535
+
536
+ **범위 (제외)**: 남은 오류의 자동 수정(사람·AI 클라이언트의 일), 테스트 실행(컴파일까지), Gradle 멀티 프로젝트
537
+
538
+ **검증 기준**: 공식 공통컴포넌트 **v4.3.2 전체 트리**(선행 조사: 루트 pom 하나의 war 모듈, 6,523 파일, Java 1.8·Spring 5.3.37·의존성 70개)를 CI 통합에서 리허설해 자동 전환·재조립 뒤 남는 컴파일 오류 수와 상위 작업을 `catalog/migration-corpus.json` 에 기대값(±10%)으로 고정합니다 — v0.37 코퍼스가 "진단 항목 수"를 고정했다면 이번에는 "남는 오류 수"를 고정합니다. 원본 지문 불변, 단계별 실패 경로(가짜 runner), `keepWorkspace` 정리 확인.
539
+
540
+ **완료 정의**: 도구 31종, `docs/design-migration.md` 「리허설」 절, README 흐름 예시를 "진단 → 평가서 → 리허설 → 적용" 으로 갱신
541
+
542
+ **결과(2026-10-08) — 완료**: `src/rehearse.ts`. 실측(공식 공통컴포넌트 v4.3.2 전체 트리, 6,523 파일, 전 과정 약 1분): 재조립(원본 v4.3.2 식별, 교체 2,258·추가 69·삭제 62) → 전환 적용(자동 61건, 수동 38건 남음) → 컴파일 오류 **6,003건**(CI·JDK 17; 로컬 JDK 21 은 6,335건, 파일 503 — 4건을 뺀 전부가 누락 패키지 `jakarta.annotation`·`org.egovframe.rte.ptl.reactive.validation`·`jakarta.websocket`… 와 그 연쇄) → pom 맞춤(parent `egovframe-web-config-parent` 5.0.2 추가, 좌표 25 추가, 버전 44·scope 3 정리) → **1건**(`EgovCertInfoUtil` — GPKI 벤더 jar 가 `javax.servlet` 을 참조). 계획과 달라진 점: (a) **단계 순서를 재조립 → 적용으로 바꿨습니다** — 계획대로 적용을 먼저 하면 적용이 컴포넌트 소스를 바꿔 원본 태그가 v5.0.1 로 오인되고 1,489개 파일이 사용자 수정으로 분류됩니다(실측). (b) 자동 단계 뒤 오류의 대부분이 pom 의존성이라 **pom 맞춤 단계**(`align-pom`, 사본 전용, 공식 고정 태그 pom 기준)를 더해 "코드 작업"을 분리했습니다. (c) javac 기본 상한(100)을 피하려고 Maven 은 fork + `-Xmaxerrs` 로 셉니다. 첫 CI 실행에서 JDK 17 이 같은 원인을 JDK 21 과 다른 모양으로 보고해(없는 패키지 import 대신 사용처마다 `cannot find symbol`, 다른 오류 때문에 Lombok 이 멈춰 getter 가 없음) 누락 의존성 비율이 15% 로 나왔으므로, 분석이 파일의 import 문·타입 선언 파일·Lombok 사용으로 원인을 묶게 고쳤고(두 JDK 모두 99.9%), pom 에 소스 인코딩이 없으면 `-Dproject.build.sourceEncoding=UTF-8` 을 줍니다(JDK 17 + POSIX 로케일의 한글 주석 오류 방지). (d) 리허설 결과는 프로젝트 안 파일이 아니라 캐시 디렉터리(`EGOVFRAME_CACHE_DIR/rehearsal/records`)에 남기고 평가서가 읽습니다(프로젝트 읽기 전용 유지). 함께 고친 결함: 4.x 의 `org.egovframe.rte` 좌표를 5.x 세대로 분류하던 문제(재조립 후보 태그가 5.x 로만 좁혀짐), Maven 출력의 `[WARNING]`·린트 경고 줄을 컴파일 오류로 세던 문제(`build_egovframe_project`·`verify` 에도 영향).
543
+
544
+ ### v0.42.0 — 폐쇄망(망분리) 운영: 오프라인 번들
545
+
546
+ **목표**: 공공 개발 환경은 대부분 망분리라 인터넷 PC 에서 받은 자료를 반입해 씁니다. 지금은 OSV 조회·공통컴포넌트 아카이브·Initializr zip·재조립 원본 태그·upstream 대조가 실행 시점에 인터넷을 씁니다. 반입 가능한 번들 하나로 같은 결과를 내게 합니다.
547
+
548
+ **범위 (포함)**
549
+ - 새 도구 `prepare_egovframe_offline_bundle(outputDir, include=["osv","components","templates","origin"], dryRun=true)`(32번째, 인터넷 쪽에서 실행): OSV Maven 전체 덤프(`osv-vulnerabilities/Maven/all.zip`)·고정 공통컴포넌트 아카이브(카탈로그 sha256 검증분)·Initializr zip 템플릿·재조립용 원본 태그 bare 미러를 한 디렉터리에 담고 `bundle-manifest.json`(파일별 sha256·크기·출처 URL·받은 시각·서버 버전)을 씁니다.
550
+ - 폐쇄망 쪽: `EGOVFRAME_OFFLINE_BUNDLE=<dir>` 가 있으면 모든 다운로드가 번들에서 먼저 찾고(지문 검증, 불일치 거부), `offline=false` 의 OSV 조회(`check_egovframe_dependencies`·`generate_egovframe_sbom`·`check_egovframe_sbom`·평가서)는 로컬 덤프로 대체합니다 — `affected[].versions` 목록 대조를 기본으로, 목록 없이 `ECOSYSTEM` 범위만 있는 레코드는 Maven 버전 비교로. 결과에 "OSV 스냅숏 YYYY-MM-DD 기준"을 표시하고 기준일이 오래되면(기본 30일) 경고합니다.
551
+ - `diagnose_egovframe_network` 의 처방에 번들 경로를 추가하고, CLI `bundle` 명령과 `server.json` 환경변수 `EGOVFRAME_OFFLINE_BUNDLE` 을 더합니다.
503
552
 
504
- ## 다음 버전 기획 (v0.38–v0.40)
553
+ **범위 (제외)**: Maven 의존성 jar 미러링(기관 Nexus·Artifactory 미러 설정 안내만), 번들 서명(manifest sha256 으로 대신, 요청 시 추가)
505
554
 
506
- v0.37.0 까지 끝난 상태(2026-10-05, 도구 28종, 테스트 약 1,500건, npm 0.36.1 까지 자동 배포·0.37.0 배포 진행)에서 다음 세 릴리스를 아래 순서로 진행합니다. 공통 원칙은 이전과 같습니다 — 규칙은 데이터로, 근거는 공식 저장소에서, 쓰기 도구는 dryRun·transaction, 각 버전은 "완료 정의"를 만족해야 릴리스합니다. 이번 세 버전의 공통 주제는 **평가에서 실행으로** 입니다 — 평가서가 가장 큰 수작업으로 지목한 공통컴포넌트 재조립을 도구가 수행하고(v0.38), 같은 분석을 AI 클라이언트 없이 CI 와 배치에서 돌리며(v0.39), SBOM 을 만드는 데서 그치지 않고 제출물로서 검증·비교·추적합니다(v0.40). 선행 조사 근거는 맨 아래 "선행 조사 결과 (2026-10-05)"에 있습니다.
555
+ **검증 기준**: 같은 날 받은 덤프로 공식 web 템플릿 SBOM(67 component)의 로컬 OSV 결과가 온라인 `querybatch` 결과와 같은 ID 집합(차이가 있으면 원인 — 철회·범위 표기 — 를 기록), 프록시를 막은 상태에서 프로젝트 생성(zip 템플릿)·컴포넌트 조립·재조립·SBOM 점검이 번들만으로 성공, 지문이 다른 번들 파일 거부
556
+
557
+ **완료 정의**: 도구 32종, README 「폐쇄망에서 쓰기」 절, `docs/design-offline-bundle.md`
558
+
559
+ ### v0.43.0 — CI·제출 형식 다변화: Jenkins·GitLab, SPDX
560
+
561
+ **목표**: 공공 사업의 빌드 서버는 GitHub Actions 보다 Jenkins·GitLab 이 흔하고, SBOM 제출 형식은 아직 특정되지 않아(가이드라인 1.0 은 SPDX·CycloneDX 를 모두 언급) SPDX 를 요구받을 수 있습니다. v0.39 의 게이트와 v0.40 의 점검을 그 환경까지 넓힙니다.
562
+
563
+ **범위 (포함)**
564
+ - `generate_egovframe_ci(platform="github"|"gitlab"|"jenkins")`: `.gitlab-ci.yml`·`Jenkinsfile`(Declarative)로 빌드·테스트와 공급망 게이트(같은 CLI 명령 — `sbom --write` → `sbom-check --fail-on minimum` → `assess --fail-on <failOn>`, 종료 코드 2 를 실패로, 아티팩트 보관). 사내 npm 레지스트리·오프라인 번들 경로를 변수로 받습니다. 게이트 job 에 `sbom-check` 단계를 추가하는 것은 GitHub 쪽에도 같이 적용합니다.
565
+ - SPDX: `generate_egovframe_sbom(bomFormat="spdx-json")` — CycloneDX 문서를 SPDX **2.3** JSON 으로 변환(패키지·`DEPENDS_ON` 관계·purl `externalRefs`·`supplier`·`creationInfo.creators`·`created`), `check_egovframe_sbom` 이 SPDX 2.3 입력도 읽어 최소 요소 7종을 SPDX 필드에 대응시켜 점검(VEX 는 CycloneDX 로만).
566
+ - 테스트: 생성 `.gitlab-ci.yml` 을 GitLab 공식 CI JSON 스키마로, SPDX 출력은 공식 `spdx-spec` v2.3 스키마로 검증(v0.40 과 같이 테스트 고정물 + ajv).
567
+
568
+ **범위 (제외)**: SPDX 3.0, 그 밖의 CI(Azure DevOps·Bamboo 등), Jenkins 플러그인 설치·서버 설정
569
+
570
+ **검증 기준**: 공식 web 템플릿 SBOM 을 SPDX 로 바꿔도 최소 요소 7종 판정과 component 수가 같고 SPDX 2.3 스키마 통과, 생성 `.gitlab-ci.yml` 스키마 통과, `Jenkinsfile` 은 Declarative 구조(pipeline·agent·stages·post) 단언 — 실제 Jenkins 실행은 보류 항목으로 남깁니다
571
+
572
+ **완료 정의**: 도구 32종(변화 없음), README 「CI 에서 쓰기」 에 GitLab·Jenkins 예시와 「SBOM 제출 준비」 에 SPDX, `docs/design-cli.md` 플랫폼 절
573
+
574
+ ### 보류·운영 항목
575
+
576
+ - **브랜치 보호(소유자 1회)**: `main` 에 "Require status checks to pass"(gate 6 + integration)를 켜면 실패 상태 병합이 재발하지 않습니다. `gh api -X PUT repos/EricSeokgon/egovframe-scaffold-mcp/branches/main/protection --input protection.json`(`required_status_checks.contexts` 에 체크 이름 7개, `enforce_admins: false`, `required_pull_request_reviews: null`, `restrictions: null`).
577
+ - **Initializr upstream**: 최신 태그 `v5.0.6`(`84d3682`)과 `main`(`bc18641`) 모두 템플릿 pom 의 parent 가 여전히 5.0.0 입니다(공식 parent 최신 5.0.2). 기존 3건과 함께 upstream 이슈로 제출할 가치가 있습니다.
578
+ - **sbom-live 첫 실패**: v0.40 PR 의 첫 CI 실행에서만 최소 요소 단언이 실패했고 재현되지 않았습니다. 테스트가 빠진 요소를 `info:` 로그로 남기므로 다시 나오면 원인을 기록합니다.
579
+ - **기준 없음 잔여**: `xerces:xercesImpl` 은 공개 기준이 없어 그대로 둡니다.
580
+ - **응답 본문 영문화**: 평가서·점검 결과의 라벨·헤더 영문화(`--lang en`)는 계속 후순위입니다.
581
+ - **MCP 신기능**: `tasks`(장시간 도구의 비동기 실행)는 SDK 에서 실험 단계라 보류 — v0.41 리허설이 수 분 걸리는 첫 도구라 안정화되면 가장 먼저 적용합니다. `elicitInput` 은 dryRun 패턴으로 충분해 쓰지 않습니다.
582
+
583
+ ### 선행 조사 결과 (2026-10-07)
584
+
585
+ - **upstream 태그**: `egovframe-runtime` 최신 v5.0.2-Final(동봉 규칙과 같음), `egovframe-common-components` v5.0.7(2026-10-07 `7912e13` 으로 재태깅 — v0.39 에서 반영), `egovframe-vscode-initializr` 최신 태그 v5.0.6(`84d3682`)·`main` `bc18641`(고정 commit `f8f5725`), 템플릿 pom parent 는 v5.0.6·`main` 모두 5.0.0.
586
+ - **리허설 대상**: 공식 공통컴포넌트 v4.3.2 는 루트에 `pom.xml` 하나(`war`)인 단일 Maven 모듈로 6,523 파일, `java.version` 1.8·Spring 5.3.37·`<dependency>` 70개 — 그대로 JDK 17 컴파일까지 가는 실자산 리허설에 쓸 수 있습니다(v0.38 재조립 테스트가 쓰는 v5.0.7 아카이브·원본 태그 미러를 재사용).
587
+ - **OSV 오프라인 덤프**: `https://osv-vulnerabilities.storage.googleapis.com/Maven/all.zip` 은 10,356,728 bytes(2026-10-06 19:47 GMT 갱신, 매일 갱신)이고 레코드 7,168건·해제 24MB 입니다. `affected` 의 범위는 `ECOSYSTEM` 13,321·`SEMVER` 121 이며 11,700 개 `affected` 에 명시 버전 목록이 있어 대부분 목록 대조만으로 판정됩니다. 표본으로 `org.springframework:spring-webmvc` 6.2.11 은 로컬 덤프에서 12건이 나옵니다(온라인과의 일치는 v0.42 검증 기준).
588
+ - **SPDX**: `spdx/spdx-spec` 태그 `v2.3` 의 `schemas/spdx-schema.json`(45,312 bytes)을 테스트 고정물로 쓸 수 있습니다.
589
+ - **도구 의존성**: `@modelcontextprotocol/sdk` 최신 1.32.1(2026-10-05, 동봉과 같음). `@cyclonedx/cyclonedx-library` 10.3.0 은 Node ≥20.18 이라 게이트(Node 18 포함)에서는 v0.40 처럼 ajv + 공식 스키마 고정물 방식을 유지합니다.
590
+
591
+ ## 이전 기획 (v0.38–v0.40, 완료)
592
+
593
+ v0.37.0 까지 끝난 상태(2026-10-05, 도구 28종, 테스트 약 1,500건, npm 0.36.1 까지 자동 배포·0.37.0 배포 진행)에서 다음 세 릴리스를 아래 순서로 진행했습니다. 세 버전 모두 2026-10-07 까지 완료했으며 기획 원문은 기록으로 남기고 결과를 항목 아래에 적었습니다. 공통 원칙은 이전과 같습니다 — 규칙은 데이터로, 근거는 공식 저장소에서, 쓰기 도구는 dryRun·transaction, 각 버전은 "완료 정의"를 만족해야 릴리스합니다. 이번 세 버전의 공통 주제는 **평가에서 실행으로** 입니다 — 평가서가 가장 큰 수작업으로 지목한 공통컴포넌트 재조립을 도구가 수행하고(v0.38), 같은 분석을 AI 클라이언트 없이 CI 와 배치에서 돌리며(v0.39), SBOM 을 만드는 데서 그치지 않고 제출물로서 검증·비교·추적합니다(v0.40). 선행 조사 근거는 맨 아래 "선행 조사 결과 (2026-10-05)"에 있습니다.
507
594
 
508
595
  ### v0.38.0 — 공통컴포넌트 재조립 실행 (`reassemble_egovframe_components`)
509
596
 
@@ -560,7 +647,7 @@ v0.37.0 까지 끝난 상태(2026-10-05, 도구 28종, 테스트 약 1,500건, n
560
647
 
561
648
  **결과(2026-10-07) — 완료**: `src/sbom-check.ts`(규칙 `catalog/sbom-rules.json` — 요소마다 인정 필드 경로를 데이터로 두고 의존관계·생성 시각만 특수 검사)와 `generate_egovframe_sbom` 의 메타데이터 옵션. 실측: 공식 web 템플릿에 cyclonedx-maven-plugin 2.9.3 을 실제로 돌린 SBOM(67 component, 계획의 69 는 v0.36 당시 해석 수)은 **공급자 50/68**(주 component·RTE·AspectJ·Micrometer 등 pom `<organization>` 이 없는 18종)과 **작성자**(플러그인은 `metadata.tools` 만 기록)가 비어 "보완 필요"였고, 나머지 5요소는 68/68 이었습니다. 계획과 달라진 점: (a) 라이브러리 공급자는 옵션으로 채울 수 없어 **공급자 표**(`suppliers`, groupId 접두어 10건, 항목마다 pom 근거)를 두고 비어 있는 component 만 채우며 `egovframe:supplierBasis=catalog` 로 출처를 남깁니다 — 이 표와 `supplier`·`author` 로 다시 만들면 7요소 모두 충족합니다. (b) 주 component 이름은 pom `<name>` 대신 artifactId 를 그대로 두고(`componentName` 으로만 덮어씀), (c) `fillSuppliers` 기본값은 `enrich` 를 따릅니다(enrich=false 면 문서를 원형 그대로). (d) SBOM 에 `vulnerabilities[]` 가 없으면(offline 생성) 재조회 결과를 "새로 알려진 것"이 아니라 "기록되지 않은 것"으로 표시합니다. (e) VEX 는 판단이 끝난 ID 에 새 영향 component 가 생기면 그 판단을 넓히지 않고 같은 ID 의 새 `in_triage` 항목을 만듭니다. 생성 SBOM·VEX 는 CycloneDX specification 1.6.1 공식 JSON 스키마로 테스트에서 검증합니다(스키마는 테스트 고정물, 패키지 미포함). CLI 명령 `sbom-check` 를 더했습니다(지표 `minimum`·`newVulns` 등, `--write` 로 VEX).
562
649
 
563
- ### 보류·운영 항목
650
+ ### 보류·운영 항목 (당시)
564
651
 
565
652
  - **브랜치 보호(소유자 1회)**: `main` 에 "Require status checks to pass"(gate 6 + integration)를 켜면 #33·#36 같은 실패 상태 병합이 재발하지 않습니다. `gh api -X PUT repos/EricSeokgon/egovframe-scaffold-mcp/branches/main/protection --input protection.json`(`required_status_checks.contexts` 에 체크 이름 7개, `enforce_admins: false`, `required_pull_request_reviews: null`, `restrictions: null`)으로 한 번에 설정할 수 있습니다.
566
653
  - **MCP Registry 등록 확인**: v0.37.0 자동 배포(Release #4)가 첫 게시입니다. 실패하면 다음 `main` 병합의 Release 가 Registry 단계만 다시 시도합니다(v0.37 재개 판정).
@@ -797,6 +884,7 @@ v0.28.1 까지의 상태에서 다음 세 릴리스를 아래 순서로 진행
797
884
 
798
885
  ## 변경 이력
799
886
 
887
+ - **0.41.0** — 전환 리허설(도구 30 → 31종). (1) 새 도구 `rehearse_egovframe_migration(projectDir, steps=["reassemble","migrate","verify","align-pom"], components?, sourceTag="auto", keepWorkspace=false, timeoutMs=900000, topN=20, format)`: `src/rehearse.ts` — 사본(`EGOVFRAME_CACHE_DIR/rehearsal`, 빌드 산출물·VCS·백업 제외, 작업 디렉터리가 프로젝트 안이면 거부)에서 재조립 → 전환 적용 → 컴파일(Maven 은 `-Dmaven.compiler.fork=true`·`JDK_JAVAC_OPTIONS=-Xmaxerrs 100000`) → `alignPomToReference`(공식 공통컴포넌트 고정 태그 pom 기준: parent 추가·교체, 누락 좌표 추가(test 제외), 버전·scope 정렬, parent 가 정의하는 속성의 프로젝트 재정의 삭제, 주석·dependencyManagement·plugin·profiles 제외, 멱등) → 컴파일. `analyzeErrors`(누락 패키지 — "package X does not exist"·symbol 의 package·파일 import 문으로 찾은 타입의 패키지, 연쇄 — 같은 파일·누락 패키지가 있는 파일이 선언한 타입의 멤버·Lombok 생성 멤버, 기타; 연쇄는 파일의 첫 누락 패키지에 귀속, 후보 좌표 `packageHint`, 기타 오류는 수동 항목·규칙과 연결, 디렉터리·파일 상위), 작업 목록(pom 맞춤이 없애는 오류 수 → 누락 패키지 → 파일), pom 패치(400줄 상한), 실행 전후 지문(`fingerprintTree`)으로 원본 불변 확인, 단계 실패는 기록하고 계속, 최근 결과를 `rehearsal/records/<경로 해시>.json` 에 저장. 도구 메타 비읽기·비파괴·멱등·openWorld, outputSchema(10종), 영문 설명, AGENTS.md 도구 목록. (2) 평가서 6절에 최근 리허설 실측(등급 산식과 별개), 맺음말의 재조립 안내를 `reassemble_egovframe_components`(전환 적용보다 먼저)로 정정. (3) CLI `rehearse`(지표 `errors`·`automation`·`aligned`·`files`·`unlinked`·`failedSteps`). (4) 수정: `parseBuildErrors` 가 Maven `[WARNING]`·javac 린트 경고(`[removal]` 등) 줄을 오류로 세던 문제와 fork 형식의 `error:` 접두, 4.x 의 `org.egovframe.rte` 좌표(버전 4.*)를 5.x 세대로 분류하던 문제(재조립 후보 태그가 5.x 로만 좁혀짐). `runBuild(extraArgs, env)`·`ResolvedCommand.env`, pom 에 소스 인코딩이 없으면 리허설 컴파일에 UTF-8. (5) 테스트: 신규 `test:rehearse` 42단언(게이트)·`test:rehearse-live`(CI 통합: 공식 v4.3.2 전체 트리 리허설 기대값 `catalog/migration-corpus.json` 의 `rehearsal`), `test:output-schemas` 54·`handshake` outputSchema 10종·`test:cli` 명령 9종. 설계: [docs/design-migration.md](docs/design-migration.md) 리허설 절. `server.json` 0.41.0.
800
888
  - **0.40.0** — SBOM 운영: 최소 요소·비교·VEX(도구 29 → 30종). (1) 새 도구 `check_egovframe_sbom(projectDir, sbomPath="sbom/bom.cdx.json", baselinePath?, offline=true, vex=false, vexPath="sbom/vex.cdx.json", dryRun=false, format)`: `src/sbom-check.ts` — 최소 요소 7종(`catalog/sbom-rules.json`: 공급자·구성요소명·버전·고유식별자·의존관계·작성자·생성 시각, 출처 NTIA 2021·SW 공급망 보안 가이드라인 1.0)을 주 component 포함 component 마다 세어 `ready`/`needs-work`·빠진 component(최대 20)·힌트, purl 만으로 v0.34 분류기 재판정(SBOM 의 `egovframe:status` 와 다른 것)과 OSV 재조회(`newIds`·`goneIds`, SBOM 에 기록이 없으면 "미기록"으로 표시), `baselinePath` 비교(groupId:artifactId 기준 추가·제거·버전·판정 변화·새/사라진 취약점), `vex=true` 면 CycloneDX VEX(`analysis.state=in_triage`, `affects` 는 BOM-Link `urn:cdx:<serial>/<version>#<bom-ref>`, serial 이 없으면 bom-ref) — 기존 VEX 의 판단·임의 필드 보존, in_triage 항목은 affects 덧붙임, 판단 끝난 ID 의 새 영향은 별도 항목, 탐지되지 않는 ID 는 지우지 않고 `notDetected`, 변경 없으면 파일 그대로, 깨진 VEX 는 중단. SBOM 은 바꾸지 않고 VEX 만 transaction 으로 기록. 도구 메타 비읽기·비파괴·openWorld, outputSchema(9종), 영문 설명, AGENTS.md 도구 목록. (2) `generate_egovframe_sbom(supplier, author, componentName, componentVersion, fillSuppliers)`: `metadata.supplier`·주 component `supplier`·`metadata.authors`(기본 pom `<organization><name>`), `metadata.lifecycles`(build), 공급자 없는 component 를 공급자 표로 보완(`egovframe:supplierBasis=catalog`), 응답 `minimum` 요약. `enrichBom` 재실행이 `egovframe:status·basis·baseline` 만 갱신하도록(다른 `egovframe:*` 보존). (3) 평가서 5절에 최소 요소 판정·VEX 항목/상태/마지막 점검 시각. (4) CLI `sbom-check`(지표 `minimum`·`gaps`·`changed`·`vulnerabilities`·`newVulns`·`added`·`removed`·`versionChanged`·`diffVulns`·`triage`, `--write` → VEX, `--vex` 직접 지정 차단). (5) 테스트: 신규 `test:sbom-check` 54단언(게이트, 실제 플러그인 출력 고정물·CycloneDX 1.6.1 공식 스키마 검증 — devDependencies `ajv`·`ajv-formats`), `test:sbom` 41·`test:cli` 36·`test:assessment` 57·`test:output-schemas` 53 단언, `handshake` outputSchema 9종, `test:sbom-live` 에 실제 SBOM 최소 요소·보강·비교·OSV VEX·판단 보존. 설계: [docs/design-dependency-check.md](docs/design-dependency-check.md) SBOM 운영 절. `server.json` 0.40.0.
801
889
  - **0.39.0** — CLI 모드 + CI 공급망 게이트(도구 29종 유지). (0) upstream 이 2026-10-07 `v5.0.7` 태그를 `3756ab2` → `7912e13` 으로 옮겨(README·권한 그룹·북마크 메뉴 컨트롤러·메뉴 이동 JSP 수정 3파일 추가) 카탈로그 고정 검증이 실패했으므로 컴포넌트 카탈로그(아카이브 sha256 `a1a4b9a7…`, 46,904,430 bytes, 6,604 files)와 전환 규칙의 `components.toCommit` 을 새 커밋으로 재고정했습니다(판정·코퍼스 기대값 변화 없음). (1) `src/cli.ts`: `npx egovframe-scaffold-mcp <assess|check|sbom|migrate|validate|diagnose|network> [옵션]` — 인자 없는 실행은 기존 stdio 서버(`index.ts` 분기), 명령은 같은 프로세스의 서버에 `InMemoryTransport` 로 붙은 클라이언트가 `tools/call`(입력 검증·허용 root·outputSchema 검증 동일). 옵션은 도구 파라미터 이름(kebab-case, `tools/list` JSON Schema 로 형 변환), 공통 `--project`·`--json`(structuredContent)·`--out`·`--step-summary`·`--fail-on`·`--write`, 쓰기 옵션 차단(`migrate --apply`·`sbom --dry-run` 등). `--fail-on` 식(`metric`·`metric>N`·`metric>=N`·`grade:C`, 명령별 지표), 종료 코드 0/2/3/64, 본문 stdout·한 줄 요약 stderr, `--help`·`--version`. (2) `generate_egovframe_ci(supplyChain, failOn="supplyChain:D", osv=true)`: SBOM → 평가서(`--step-summary`) → 아티팩트(`if: always()`) 게이트 job, 패키지 버전 고정, `failOn` 형식 제한(`CI_FAIL_ON_RE`, YAML 주입 차단). (3) `@modelcontextprotocol/sdk` 1.29 → 1.32.1. (4) 테스트: 신규 `test:cli` 32단언(게이트), `test:ci` 공급망 게이트·주입 거부, CI 통합에 생성 워크플로 `actionlint` 1.7.7 검사와 코퍼스 트리 CLI 평가서(job 요약). 설계: [docs/design-cli.md](docs/design-cli.md). `server.json` 0.39.0.
802
890
  - **0.38.0** — 공통컴포넌트 재조립 실행(도구 28 → 29종). (1) 새 도구 `reassemble_egovframe_components(projectDir, components?, sourceTag="auto", database?, dryRun=true, verify=false, timeoutMs, format)`: `src/component-origin.ts`(공식 `egovframe-common-components` 의 blob 없는 bare 미러 `GitOriginSource` — 태그별 `--depth 1 --filter=blob:none` fetch·`ls-tree`·지연 `cat-file`, 테스트용 `MemoryOriginSource`, `gitBlobId`·CRLF 정규화 `blobIdsOf`, 캐시 `EGOVFRAME_CACHE_DIR`)와 `src/reassemble.ts`(대상 컴포넌트 선정 — 감지 컴포넌트·그룹 펼침·매니페스트 관리 컴포넌트 거부, 최장 접두어 소유 판정, 좌표 세대별 후보 태그 점수·동점은 높은 버전·짧은 이름, 8가지 판정과 5가지 처리, 목표 내용의 blob 검증, 하나의 transaction 으로 교체·추가·삭제·백업·자산 참고본·패치·SQL·매니페스트·`reassemble-plan.json`, `verify` 컴파일 오류를 작업 목록에 연결, Markdown·outputSchema). 도구 메타 destructive·openWorld(5종), 영문 설명, `server.json` 환경변수 `EGOVFRAME_CACHE_DIR`. `ProjectFileTransaction.removeFile`(rollback 시 복원), `buildComponentSqlPlan` 분리(조립·재조립 공유). (2) **공통컴포넌트 v5.0.7**: 컴포넌트 카탈로그(`generate-catalog.mjs --archive-sha256/--archive-bytes` 로 codeload 아카이브 지문 고정, sha256 `44a518c1…`, 46,903,152 bytes, 6,604 files), 전환 규칙 `components.toTag` v5.0.7(제거 55·이동 4), `migrate_egovframe_project` 의 재조립 권고가 새 도구를 안내. (3) 수정: `upgrade_egovframe_project` 가 컴포넌트 자산 파일(메시지·IDGN·웹 자산 등)을 upstream 비교에 넣지 않아 전부 "removed" 로 보고하던 결함, `sync_egovframe_catalog` 의 GitHub API 호출이 만료 토큰(401)이면 비인증으로 재시도. 0.37.0 배포 전 `main` 에 들어간 CI 수정(카탈로그 검증 GitHub API 인증, `proxy-addr` 2.0.8, drift 감시 비차단·탐침 재시도)도 이 버전 기록에 남깁니다. (4) 테스트: 신규 `test:reassemble` 46단언(게이트)·`test:reassemble-live`(CI 통합), `test:migrate-corpus` 에 공식 트리 재조립 미리보기(원본 100%·사용자 수정 0), `output-schemas`·`handshake` 29종·outputSchema 8종·destructive 5종, `registry` 환경변수 3종. 설계: [docs/design-migration.md](docs/design-migration.md) 재조립 절. `server.json` 0.38.0.
@@ -187,5 +187,26 @@
187
187
  "version": "0.37.0",
188
188
  "rulesTag": "v5.0.2-Final",
189
189
  "baselineSurveyedAt": "2026-10-02"
190
+ },
191
+ "rehearsal": {
192
+ "description": "전환 리허설(v0.41) 기대값 — 공식 공통컴포넌트 v4.3.2 전체 트리(단일 war 모듈)를 rehearse_egovframe_migration 으로 돌린 결과. test/rehearse-live.mjs 가 단언한다(CI 통합, JDK 17·Maven).",
193
+ "tag": "v4.3.2",
194
+ "commit": "bfa2ef5bf80ac9079f9c409af88ecf407b574c55",
195
+ "files": 6523,
196
+ "tolerance": {
197
+ "pct": 0.1
198
+ },
199
+ "expected": {
200
+ "origin": "v4.3.2",
201
+ "reassembleReplace": 2258,
202
+ "reassembleAdd": 69,
203
+ "reassembleDelete": 62,
204
+ "migrateApplied": 61,
205
+ "afterAutomationErrors": 6003,
206
+ "afterAutomationMissingShare": 0.95,
207
+ "afterPomAlignmentMaxErrors": 5
208
+ },
209
+ "measuredAt": "2026-10-08",
210
+ "measuredWith": "CI(ubuntu·JDK 17) 6,003건 · 로컬 JDK 21.0.10 6,335건 — javac 판본마다 연쇄 보고가 달라 ±10% 로 둔다, Maven 3.9.11"
190
211
  }
191
212
  }
package/dist/agents-md.js CHANGED
@@ -95,7 +95,7 @@ const T = {
95
95
  "의존성 버전을 올릴 때는 `check_egovframe_dependencies` 로 공식 5.x parent 기준과 대조하고, parent 가 관리하는 좌표에는 `<version>` 을 적지 않습니다.",
96
96
  ],
97
97
  tools: "## 사용할 수 있는 MCP 도구", toolsNote: `이 프로젝트는 \`egovframe-scaffold-mcp\`(v${SERVER_VERSION}) 로 관리할 수 있습니다. 진단·검증은 읽기 전용이고, 쓰기 도구는 dryRun 과 transaction(실패 시 복구)을 제공합니다.`,
98
- toolRows: ["`diagnose_egovframe_project` · `validate_egovframe_project` · `generate_egovframe_report` — 진단·검증·리포트", "`add_egovframe_components` · `remove_egovframe_components` · `upgrade_egovframe_project` — 공통컴포넌트 수명주기", "`generate_egovframe_crud` · `generate_egovframe_config` · `generate_egovframe_ci` — 코드·설정·CI 생성", "`build_egovframe_project` · `test_egovframe_project` — 빌드·테스트 실행과 오류 구조화", "`migrate_egovframe_project` · `check_egovframe_dependencies` — 5.x 전환·의존성 점검", "`generate_egovframe_sbom` · `check_egovframe_sbom` — SBOM 생성·점검(최소 요소·이전 SBOM 비교·VEX)", "`diagnose_egovframe_network` — 다운로드 실패 시 네트워크 진단"],
98
+ toolRows: ["`diagnose_egovframe_project` · `validate_egovframe_project` · `generate_egovframe_report` — 진단·검증·리포트", "`add_egovframe_components` · `remove_egovframe_components` · `upgrade_egovframe_project` — 공통컴포넌트 수명주기", "`generate_egovframe_crud` · `generate_egovframe_config` · `generate_egovframe_ci` — 코드·설정·CI 생성", "`build_egovframe_project` · `test_egovframe_project` — 빌드·테스트 실행과 오류 구조화", "`rehearse_egovframe_migration` · `reassemble_egovframe_components` · `migrate_egovframe_project` — 5.x 전환(사본 리허설 → 재조립 → 적용)", "`check_egovframe_dependencies` — 의존성 점검", "`generate_egovframe_sbom` · `check_egovframe_sbom` — SBOM 생성·점검(최소 요소·이전 SBOM 비교·VEX)", "`diagnose_egovframe_network` — 다운로드 실패 시 네트워크 진단"],
99
99
  generated: (d) => `_생성: ${d} · egovframe-scaffold-mcp generate_agents_md. 프로젝트가 바뀌면 다시 생성하세요._`,
100
100
  backups: (dirs) => `현재 백업 디렉터리가 있습니다: ${dirs.join(", ")}`,
101
101
  },
@@ -121,7 +121,7 @@ const T = {
121
121
  "When bumping dependency versions, compare against the official 5.x parent baseline with `check_egovframe_dependencies`; do not write `<version>` for coordinates the parent manages.",
122
122
  ],
123
123
  tools: "## Available MCP tools", toolsNote: `This project can be managed with \`egovframe-scaffold-mcp\` (v${SERVER_VERSION}). Diagnosis and validation are read-only; write tools offer dryRun and transactions (rollback on failure).`,
124
- toolRows: ["`diagnose_egovframe_project` · `validate_egovframe_project` · `generate_egovframe_report` — diagnosis, validation, report", "`add_egovframe_components` · `remove_egovframe_components` · `upgrade_egovframe_project` — common-component lifecycle", "`generate_egovframe_crud` · `generate_egovframe_config` · `generate_egovframe_ci` — code, configuration and CI generation", "`build_egovframe_project` · `test_egovframe_project` — build/test execution with structured errors", "`migrate_egovframe_project` · `check_egovframe_dependencies` — 5.x migration and dependency check", "`generate_egovframe_sbom` · `check_egovframe_sbom` — SBOM generation and checks (minimum elements, diff, VEX)", "`diagnose_egovframe_network` — network diagnosis when downloads fail"],
124
+ toolRows: ["`diagnose_egovframe_project` · `validate_egovframe_project` · `generate_egovframe_report` — diagnosis, validation, report", "`add_egovframe_components` · `remove_egovframe_components` · `upgrade_egovframe_project` — common-component lifecycle", "`generate_egovframe_crud` · `generate_egovframe_config` · `generate_egovframe_ci` — code, configuration and CI generation", "`build_egovframe_project` · `test_egovframe_project` — build/test execution with structured errors", "`rehearse_egovframe_migration` · `reassemble_egovframe_components` · `migrate_egovframe_project` — 5.x migration (rehearse on a copy, reassemble, apply)", "`check_egovframe_dependencies` — dependency check", "`generate_egovframe_sbom` · `check_egovframe_sbom` — SBOM generation and checks (minimum elements, diff, VEX)", "`diagnose_egovframe_network` — network diagnosis when downloads fail"],
125
125
  generated: (d) => `_Generated ${d} by egovframe-scaffold-mcp generate_agents_md. Regenerate when the project changes._`,
126
126
  backups: (dirs) => `Backup directories currently present: ${dirs.join(", ")}`,
127
127
  },
@@ -11,6 +11,7 @@ import { migrateProject } from "./migrate.js";
11
11
  import { checkDependencies, STATUS_LABEL, BASIS_LABEL } from "./dependencies.js";
12
12
  import { DEFAULT_SBOM_PATH } from "./sbom.js";
13
13
  import { checkMinimumElements, DEFAULT_VEX_PATH } from "./sbom-check.js";
14
+ import { readRehearsalRecord } from "./rehearse.js";
14
15
  import { SERVER_VERSION } from "./version.js";
15
16
  const COUNT_BANDS = (a, b) => [{ upTo: 0, points: 0 }, { upTo: a, points: 1 }, { upTo: b, points: 2 }, { upTo: null, points: 3 }];
16
17
  /** 제거된 API 참조로 세는 전환 항목 종류 */
@@ -202,6 +203,13 @@ export async function assessProject(opts) {
202
203
  },
203
204
  security, sbom,
204
205
  grades: { migration, supplyChain },
206
+ ...(() => {
207
+ const rec = readRehearsalRecord(projectDir);
208
+ if (!rec)
209
+ return {};
210
+ const cnt = (p) => (p?.ran ? (p.success ? 0 : p.analysis?.errors ?? null) : null);
211
+ return { rehearsal: { at: rec.startedAt, afterAutomation: cnt(rec.afterAutomation), afterPomAlignment: cnt(rec.afterPomAlignment), projectUnchanged: rec.original.unchanged, origin: rec.reassemble?.origin ?? null } };
212
+ })(),
205
213
  notes,
206
214
  };
207
215
  }
@@ -299,9 +307,13 @@ export function renderAssessmentMarkdown(r) {
299
307
  L.push(``, `## 6. 등급과 근거`, ``);
300
308
  renderGrade(L, "전환 난이도", r.grades.migration);
301
309
  renderGrade(L, "공급망 상태", r.grades.supplyChain);
310
+ if (r.rehearsal)
311
+ L.push(`**실측(리허설 ${r.rehearsal.at})**: 재조립·전환 적용 후 컴파일 오류 ${r.rehearsal.afterAutomation ?? "—"}건 → pom 맞춤 후 ${r.rehearsal.afterPomAlignment ?? "—"}건${r.rehearsal.origin ? ` · 원본 태그 ${r.rehearsal.origin}` : ""} — 등급 산식과 별개로 rehearse_egovframe_migration 이 프로젝트 사본에서 잰 값입니다.`, ``);
312
+ else
313
+ L.push(`실측 없음 — \`rehearse_egovframe_migration\` 으로 프로젝트 사본에서 자동 단계 후 남는 컴파일 오류를 잴 수 있습니다.`, ``);
302
314
  renderFormula(L, MIGRATION_RUBRIC);
303
315
  renderFormula(L, SUPPLY_CHAIN_RUBRIC);
304
316
  L.push(`벤더 배포·기준 없음·버전 없음 의존성과 공통컴포넌트 수 자체는 등급에 넣지 않습니다. 비용·공수는 산정하지 않습니다 — 건수와 등급까지가 이 평가서의 범위입니다.`);
305
- L.push(``, `---`, `읽기 전용 분석입니다. 자동 항목은 \`migrate_egovframe_project\`(apply), 재조립은 \`add_egovframe_components\`, SBOM 은 \`generate_egovframe_sbom\` 으로 진행합니다.`);
317
+ L.push(``, `---`, `읽기 전용 분석입니다. 리허설은 \`rehearse_egovframe_migration\`, 재조립은 \`reassemble_egovframe_components\`(전환 적용보다 먼저), 자동 항목은 \`migrate_egovframe_project\`(apply), SBOM 은 \`generate_egovframe_sbom\` 으로 진행합니다.`);
306
318
  return L.join("\n");
307
319
  }
@@ -70,9 +70,18 @@ export function parseBuildErrors(buildTool, output) {
70
70
  // 예) [ERROR] /abs/Foo.java:[12,5] cannot find symbol
71
71
  // /abs/Foo.java:[12,5] cannot find symbol
72
72
  // [ERROR] C:\work\Foo.java:[12,5] cannot find symbol (Windows 드라이브 문자는 경로의 일부)
73
+ // [ERROR] /abs/Foo.java:[12,5] error: cannot find symbol (maven.compiler.fork=true 면 javac 형식 "error:" 접두)
74
+ // [WARNING] /abs/Foo.java:[3,1] [removal] … ← 경고는 오류가 아니다(v0.41 수정: 예전에는 "WARNING] /abs/…" 를 파일로 읽었다)
73
75
  const re = /(?:\[ERROR\]\s*)?((?:[A-Za-z]:)?[^\s\[:][^:\n]*?\.(?:java|kt|xml)):\[(\d+)(?:,(\d+))?\]\s*(.+)/g;
74
76
  let m;
75
77
  while ((m = re.exec(output)) !== null) {
78
+ const lineStart = output.lastIndexOf("\n", m.index) + 1;
79
+ const prefix = output.slice(lineStart, m.index + m[1].length);
80
+ if (/^\s*\[(WARNING|WARN|INFO|DEBUG)\]/.test(prefix) || /^(WARNING|WARN|INFO|DEBUG)\]/.test(m[1]))
81
+ continue;
82
+ // javac 린트 경고(예: "[removal] X has been deprecated")는 [WARNING] 접두가 앞 줄에 붙어 나오기도 한다
83
+ if (/^(\[[a-z-]+\]|warning:)/.test(m[4].trim()))
84
+ continue;
76
85
  const key = `${m[1]}:${m[2]}:${m[3] ?? ""}`;
77
86
  if (seen.has(key))
78
87
  continue;
@@ -81,7 +90,7 @@ export function parseBuildErrors(buildTool, output) {
81
90
  file: m[1].trim(),
82
91
  line: Number(m[2]),
83
92
  column: m[3] ? Number(m[3]) : undefined,
84
- message: m[4].trim(),
93
+ message: m[4].trim().replace(/^error:\s*/, ""),
85
94
  };
86
95
  attachJavacDetails(err, output.slice(m.index + m[0].length, m.index + m[0].length + 400));
87
96
  errors.push(err);
@@ -166,6 +175,7 @@ export const defaultRunner = (cmd, opts) => new Promise((resolve) => {
166
175
  const isWin = process.platform === "win32";
167
176
  const child = spawn(cmd.command, cmd.args, {
168
177
  cwd: cmd.cwd,
178
+ ...(cmd.env ? { env: { ...process.env, ...cmd.env } } : {}),
169
179
  shell: isWin,
170
180
  // POSIX: 새 프로세스 그룹의 리더로 띄워 그룹 단위로 종료할 수 있게 한다.
171
181
  detached: !isWin,
@@ -213,7 +223,8 @@ export async function runBuild(opts) {
213
223
  throw new Error(`빌드 파일(pom.xml·build.gradle)을 찾지 못했습니다: ${projectDir} — eGovFrame 프로젝트 루트인지 확인하세요.`);
214
224
  }
215
225
  const goal = opts.goal ?? "compile";
216
- const resolved = resolveCommand(projectDir, buildTool, goal, { platform: opts.platform });
226
+ const base = resolveCommand(projectDir, buildTool, goal, { platform: opts.platform });
227
+ const resolved = { ...base, args: [...base.args, ...(opts.extraArgs ?? [])], ...(opts.env ? { env: opts.env } : {}) };
217
228
  const command = [resolved.command, ...resolved.args].join(" ");
218
229
  if (opts.dryRun) {
219
230
  return { buildTool, goal, command, cwd: resolved.cwd, usedWrapper: resolved.usedWrapper, dryRun: true };
package/dist/cli.js CHANGED
@@ -53,6 +53,19 @@ export const COMMANDS = {
53
53
  return `${s.sbomPath} — 최소 요소 ${m.verdict === "ready" ? "제출 가능" : "보완 필요"}${o.queried ? ` · 새 취약점 ${Array.isArray(o.newIds) ? o.newIds.length : 0}` : ""}${df ? ` · 비교 +${Array.isArray(df.added) ? df.added.length : 0}/-${Array.isArray(df.removed) ? df.removed.length : 0}/버전 ${Array.isArray(df.versionChanged) ? df.versionChanged.length : 0}` : ""}${vx ? ` · VEX ${vx.written ? "기록" : "미기록"} in_triage ${num(rec(vx.states).in_triage)}` : ""}`;
54
54
  },
55
55
  },
56
+ rehearse: {
57
+ tool: "rehearse_egovframe_migration", summary: "전환 리허설(사본에서 재조립·적용·컴파일, 원본 불변)",
58
+ metrics: (s) => {
59
+ const ph = (p) => { const x = rec(p); return x.ran ? (x.success ? 0 : num(rec(x.analysis).errors)) : null; };
60
+ const a = ph(s.afterAutomation), b = ph(s.afterPomAlignment);
61
+ const last = rec(rec(s.afterPomAlignment).ran ? rec(s.afterPomAlignment).analysis : rec(s.afterAutomation).analysis);
62
+ return { errors: b ?? a ?? 0, automation: a ?? 0, aligned: b ?? 0, files: num(last.files), unlinked: num(rec(last.linked).unlinked), failedSteps: Array.isArray(s.steps) ? s.steps.filter((x) => rec(x).ran && !rec(x).ok).length : 0 };
63
+ },
64
+ line: (s) => {
65
+ const ph = (p) => { const x = rec(p); return !x.ran ? "—" : x.success ? "0" : String(num(rec(x.analysis).errors)); };
66
+ return `리허설 — 자동 단계 후 오류 ${ph(s.afterAutomation)} → pom 맞춤 후 ${ph(s.afterPomAlignment)} · 원본 ${rec(s.original).unchanged ? "불변" : "변경됨"}`;
67
+ },
68
+ },
56
69
  migrate: {
57
70
  tool: "migrate_egovframe_project", summary: "5.x 전환 진단(읽기 전용)", fixed: { apply: false, verify: false }, blocked: ["apply", "verify", "dryRun", "skipComponents"],
58
71
  metrics: (s) => { const sm = rec(s.summary); return { items: Array.isArray(s.items) ? s.items.length : 0, auto: num(sm.auto), manual: num(sm.manual), files: num(sm.files) }; },
package/dist/i18n.js CHANGED
@@ -27,6 +27,7 @@ export const TOOL_DESCRIPTIONS_EN = {
27
27
  generate_agents_md: "Generate AGENTS.md for AI coding tools from project diagnosis: build/test commands (wrapper-aware), RTE version and 5.x migration status, DbType, base packages and config directories, installed components, rules (coordinates, backup dirs, secrets, dependency baseline) and available MCP tools. Refuses existing files unless overwrite=true; dryRun returns content only; ko/en.",
28
28
  search_egovframe_docs: "Search the official guide index by keyword (offline; fetchTop>0 downloads snippets).",
29
29
  check_egovframe_sbom: "Check an existing CycloneDX SBOM as a submission artifact without changing it: (1) the 7 minimum elements (supplier, component name, version, unique identifier, dependency relationship, author, timestamp; NTIA minimum elements, same as the Korean SW supply-chain security guideline 1.0) per component with a ready / needs-work verdict, (2) re-check without any build tool from purls only: baseline verdicts that changed since generation and, with offline=false, OSV vulnerabilities newly published since generation, (3) with baselinePath, a diff against a previous SBOM (added, removed, version changes, verdict changes, new vulnerabilities), (4) with vex=true, a CycloneDX VEX draft (default sbom/vex.cdx.json) where new findings are analysis.state=in_triage referencing the SBOM via BOM-Link; human decisions in an existing VEX are preserved and only new findings are added (dryRun previews).",
30
+ rehearse_egovframe_migration: "Rehearse the 5.x migration on a copy of the project without touching it: copy (build output, VCS and backups excluded) into EGOVFRAME_CACHE_DIR/rehearsal, then (1) reassemble the common components (before apply, so the origin tag is still recognisable), (2) apply the automatic migration items, (3) compile and count every javac error (Maven forks javac with -Xmaxerrs, no 100-error cap), (4) align the copy's pom to the official common-components pom of the pinned tag (parent, missing coordinates, versions, scopes, parent-managed properties) and compile again. Reports both error counts, missing packages with candidate coordinates, the cascade errors in the same files, manual items and files that remain, a worklist ordered by errors removed, the pom patch, and proves the original is unchanged with a before/after fingerprint. The latest result is shown in the assessment report.",
30
31
  reassemble_egovframe_components: "Reassemble the common-component sources copied into a 3.x/4.x (or older 5.x) project onto the pinned catalog version (now v5.0.7). It identifies the original tag by matching git blob ids against official egovframe-common-components tags (no file contents downloaded; or pass sourceTag), classifies every file three-way (identical, unchanged, user-modified, new in 5.x, removed in 5.x, unverified, user-added), and with dryRun=false writes the target files in one transaction (archive sha256 verified), backs up and removes files that 5.x dropped, replaces user-modified sources while keeping their changes as unified-diff patches (config/asset files keep the user copy and store the target as reference), and writes the manifest so upgrade, validate and remove work afterwards. Backups, patches and reassemble-plan.json go to migration-backup/<time>-reassemble-*/, and a worklist is returned; verify=true compiles and attaches errors to worklist files. Needs git; cache in EGOVFRAME_CACHE_DIR. Patches are not re-applied automatically.",
31
32
  generate_egovframe_report: "Generate a project report in Markdown (or format=json). sections=[\"components\"] (default): installed common components, referenced tables, guide links and issues. sections=[\"assessment\"]: a 5.x migration readiness assessment — overview, migration scope (auto/manual items by kind, reassembly advice, top manual work), dependencies (baseline verdicts and actions; transitive with resolve=true; OSV vulnerabilities with offline=false), security settings, SBOM summary, and two A–D grades (migration difficulty, supply-chain state) with the scoring formula printed in the report so it can be recomputed by hand. No cost or effort estimates. outputPath saves a new file inside the project (existing files are refused); otherwise nothing is written.",
32
33
  upgrade_egovframe_project: "Upgrade installed components against upstream with 3-way comparison: preserves user modifications, dryRun by default, re-verifies before applying, single transaction for files, backup and manifest.",
package/dist/index.js CHANGED
@@ -94,6 +94,7 @@ if (isMain) {
94
94
  export { MAVEN_DEPENDENCY_PLUGIN, parseMavenCoord, parseMavenTree, parseGradleTree, dedupeArtifacts, treeCommand, resolveDependencyTree } from "./dependency-tree.js";
95
95
  export { CYCLONEDX_MAVEN_PLUGIN, CYCLONEDX_SPEC_VERSION, DEFAULT_SBOM_PATH, resolveOutputPath, resolveSbomOutputPath, sbomCommand, purlOf, pomIdentity, applySbomMetadata, fillComponentSuppliers, parentKindOf, buildBomFromTree, enrichBom, attachVulnerabilities, generateSbom, renderSbomMarkdown } from "./sbom.js";
96
96
  export { DEFAULT_VEX_PATH, loadSbomRules, valuesAt, parseMavenPurl, coordinateOf, supplierFor, checkMinimumElements, recheckSbom, diffSboms, bomLink, mergeVex, checkSbom, renderSbomCheckMarkdown } from "./sbom-check.js";
97
+ export { REHEARSAL_STEPS, JAVAC_MAX_ERRORS, fingerprintTree, copyProject, mainDependencies, alignPomToReference, missingPackageOf, packageHint, analyzeErrors, rehearsalRecordPath, readRehearsalRecord, rehearseMigration, renderRehearsalMarkdown } from "./rehearse.js";
97
98
  export { COMPONENTS_REPOSITORY, gitBlobId, blobIdsOf, tagVersionKey, compareTags, defaultCacheDir, GitOriginSource, MemoryOriginSource } from "./component-origin.js";
98
99
  export { EXIT as CLI_EXIT, COMMANDS as CLI_COMMANDS, parseArgs, kebabToCamel, coerceArgs, parseFailOn, evaluateFailOn, usage as cliUsage, runCli } from "./cli.js";
99
100
  export { REASSEMBLE_STATES, REASSEMBLE_STATE_LABEL, unifiedPatch, reassembleComponents, renderReassembleMarkdown } from "./reassemble.js";
package/dist/migrate.js CHANGED
@@ -380,7 +380,8 @@ function scanPom(rel, text, rules, push, ctx) {
380
380
  }
381
381
  if (ix.toArtifacts.has(key)) {
382
382
  ctx.hasRte = true;
383
- ctx.eras.add("5.x");
383
+ // 4.x 도 org.egovframe.rte 좌표를 쓴다 — 해석된 버전이 4.* 면 4.x (v0.41 수정: 예전에는 5.x 로 보아 재조립 후보가 5.x 태그로만 좁혀졌다)
384
+ ctx.eras.add(ver && /^4\./.test(ver) ? "4.x" : "5.x");
384
385
  if (ver && versionBelow(ver, t.runtimeVersion) === true)
385
386
  push({ file: rel, line: d.line, kind: "rte-version", from: `${key}${verText}`, to: `${key}:${t.runtimeVersion}`, action: "auto", reason: `RTE ${t.runtimeVersion} 기준`, edits: rteVersionEdit() });
386
387
  continue;
@@ -189,6 +189,7 @@ export const AssessmentOutput = loose({
189
189
  security: loose({ ok: z.number().int(), missing: z.number().int(), na: z.number().int(), checks: z.array(loose({ id: z.string(), title: z.string(), status: z.enum(["ok", "missing", "n/a"]), evidence: z.array(loose({ file: z.string(), line: z.number().int(), text: z.string() })), hint: z.string() })) }),
190
190
  sbom: loose({ present: z.boolean(), path: z.string(), components: z.number().int().optional(), specVersion: z.string().optional(), timestamp: z.string().optional(), vulnerabilities: z.number().int().optional(), minimum: loose({ verdict: z.enum(["ready", "needs-work"]), missing: z.array(z.string()) }).optional(), vex: loose({ path: z.string(), present: z.boolean(), timestamp: z.string().optional(), vulnerabilities: z.number().int().optional(), states: z.record(z.number().int()).optional() }).optional(), note: z.string() }),
191
191
  grades: loose({ migration: GradeResult, supplyChain: GradeResult }),
192
+ rehearsal: loose({ at: z.string(), afterAutomation: z.number().int().nullable(), afterPomAlignment: z.number().int().nullable(), projectUnchanged: z.boolean(), origin: z.string().nullable() }).optional(),
192
193
  notes: z.array(z.string()),
193
194
  });
194
195
  export const ReportOutput = loose({
@@ -222,6 +223,31 @@ export const ReassembleOutput = loose({
222
223
  verify: loose({ ran: z.boolean(), success: z.boolean().nullable(), command: z.string().optional(), durationMs: z.number().optional(), errors: z.number().int(), inReassembled: z.number().int(), reason: z.string().optional() }).optional(),
223
224
  notes: z.array(z.string()),
224
225
  });
226
+ // v0.41: rehearse_egovframe_migration
227
+ const ErrorAnalysisSchema = loose({
228
+ errors: z.number().int(), files: z.number().int(), missingPackage: z.number().int(), cascade: z.number().int(), other: z.number().int(),
229
+ missingPackages: z.array(loose({ package: z.string(), direct: z.number().int(), cascade: z.number().int(), errors: z.number().int(), files: z.number().int(), hint: z.string().nullable() })),
230
+ byDirectory: z.array(loose({ dir: z.string(), errors: z.number().int() })),
231
+ linked: loose({ manual: z.number().int(), rules: z.number().int(), unlinked: z.number().int() }),
232
+ capped: z.boolean(),
233
+ topFiles: z.array(loose({ file: z.string(), errors: z.number().int(), sample: z.string() })),
234
+ });
235
+ const RehearsalPhaseSchema = loose({ ran: z.boolean(), success: z.boolean().nullable(), command: z.string().optional(), durationMs: z.number().optional(), timedOut: z.boolean().optional(), analysis: ErrorAnalysisSchema.optional(), reason: z.string().optional() });
236
+ export const RehearseOutput = loose({
237
+ projectDir: z.string(),
238
+ buildTool: z.enum(["maven", "gradle"]).nullable(),
239
+ startedAt: z.string(),
240
+ workspace: loose({ path: z.string().nullable(), kept: z.boolean(), files: z.number().int(), bytes: z.number().int(), copyMs: z.number() }),
241
+ original: loose({ files: z.number().int(), bytes: z.number().int(), sha256: z.string(), unchanged: z.boolean() }),
242
+ steps: z.array(loose({ step: z.enum(["reassemble", "migrate", "verify", "align-pom"]), ran: z.boolean(), ok: z.boolean(), durationMs: z.number(), summary: z.string(), error: z.string().optional() })),
243
+ reassemble: loose({ origin: z.string().nullable(), components: z.number().int(), actions: z.record(z.number().int()), worklist: z.number().int() }).optional(),
244
+ migrate: loose({ applied: loose({ items: z.number().int(), edits: z.number().int(), files: z.number().int() }), manualRemaining: z.number().int(), byKind: z.record(z.number().int()) }).optional(),
245
+ afterAutomation: RehearsalPhaseSchema.optional(),
246
+ afterPomAlignment: RehearsalPhaseSchema.extend({ reference: z.string().optional(), parent: z.string().nullable().optional(), added: z.array(z.string()).optional(), versionsChanged: z.array(z.string()).optional(), scopesChanged: z.array(z.string()).optional(), propertiesSet: z.array(z.string()).optional(), propertiesRemoved: z.array(z.string()).optional(), patch: z.string().optional() }).optional(),
247
+ worklist: z.array(loose({ kind: z.enum(["align-pom", "missing-package", "manual-item", "file"]), title: z.string(), errors: z.number().int(), detail: z.string() })),
248
+ savedTo: z.string().optional(),
249
+ notes: z.array(z.string()),
250
+ });
225
251
  export const OUTPUT_SCHEMAS = {
226
252
  diagnose_egovframe_project: DiagnoseOutput,
227
253
  validate_egovframe_project: ValidateOutput,
@@ -230,6 +256,7 @@ export const OUTPUT_SCHEMAS = {
230
256
  diagnose_egovframe_network: NetworkOutput,
231
257
  generate_egovframe_sbom: SbomOutput,
232
258
  check_egovframe_sbom: SbomCheckOutput,
259
+ rehearse_egovframe_migration: RehearseOutput,
233
260
  generate_egovframe_report: ReportOutput,
234
261
  reassemble_egovframe_components: ReassembleOutput,
235
262
  };