rhwp-edit What this skill does CLI로 문서의 본문 텍스트 , 표 구조 , 셀 내용 을 round-trip 안전하게 수정한다. CLI는 (Rust + WebAssembly) 위에 얇은 Node 래퍼를 씌워 , , , , 같은 편집 동작을 서브커맨드로 노출한다. 결과는 항상 새 파일로 저장한다. 이 스킬은 편집 전용 이다. 문서를 Markdown/JSON으로 변환하거나 필드만 추출하려면 스킬을 사용한다. 페이지 렌더링 디버깅이나 IR 비교가 필요하면 스킬을 사용한다. When to use - "HWP 본문에 한 줄 추가해줘" - "서식은 유지한 채로 2025를 2026으로 일괄 치환해줘" - "3행 4열짜리 표를 HWP에 넣어줘" - "표의 특정 셀 내용을 바꿔줘" - "빈 HWP 새 파일을 만들어줘" When not to use - HWP → Markdown / JSON 변환 → 스킬(kordoc)을 쓴다. rhwp-edit은 바이너리 편집 전용이다. - HWPX 원본을 다시 HWPX로 저장 → rhwp v0.7.3 기준 업스트림이 으로 HWPX 저장 경로를 막아둔 상태다. HWPX를 입력으로 주면 내부적으로 HWP IR로 올라온…

...'` 같은 형식을 쓴다.\n- **`search` / `replace-all` 은 본문 문단만 스캔한다**: 업스트림 `searchText` 가 본문(body) 범위로 제한되어 있고, `k-skill-rhwp replace-all` 도 같은 스코프를 그대로 따른다. **표(cell) 안의 텍스트, 머리말/꼬리말, 각주 본문**에서는 `search` 가 `found:false` 를 돌려주고 `replace-all` 도 해당 위치를 건드리지 않는다. 셀 내용이 대상이라면 `list-paragraphs` 또는 `info` 로 표 좌표를 잡고 `set-cell-text` 로 직접 쓴다.\n- **문단 경계 / 개행 치환 금지**: `replace-all` 은 한 문단 안에서의 치환만 보장한다. `--replacement` 에 개행(`\\n`, `\\r`, U+2028, U+2029) 이 들어오면 CLI 는 exit code 1 과 \"replacement must not contain newline or paragraph-break characters\" 메시지를 돌려준다. 여러 문단을 만들고 싶으면 `insert-text` 를 여러 번 호출한다.\n- **치환은 원본 매칭 기준 non-overlapping**: 예를 들어 query `a` / replacement `aa` / 원본 `aaa` 는 원본의 각 `a` 를 한 번씩 교체해 `aaaaaa` 가 된다. 치환으로 새로 들어온 문자열은 다시 매칭하지 않는다.\n- **대소문자 무시 매칭은 UTF-16 길이가 보존되는 문자에만 안전하다**: 기본값인 대소문자 무시(`--case-sensitive` 없이) 모드는 `String.prototype.toLowerCase()` 가 UTF-16 길이를 그대로 유지한다는 전제 위에서 오프셋을 계산한다. 터키어 `İ`(U+0130) 처럼 소문자화 시 `i` + 결합 점(U+0307) 로 길이가 늘어나는 문자가 본문 또는 쿼리에 포함되면, 조용한 문서 손상을 방지하기 위해 `replace-all` 이 exit code 1 과 함께 `case-insensitive matching is unsafe because case folding changes the UTF-16 length` 메시지를 돌려준다. 이런 문서에는 `--case-sensitive` 로 다시 실행하거나, 입력을 미리 정규화한다. 한글·ASCII 본문에는 해당하지 않으며, `2025 → 2026` 같은 실제 사업 신청서 워크플로우는 아무 영향을 받지 않는다.\n\n## Notes\n\n- 업스트림 rhwp: https://github.com/edwardkim/rhwp\n- 업스트림 `@rhwp/core` npm: https://www.npmjs.com/package/@rhwp/core\n- 업스트림은 활발히 개발 중이다(v0.7.3 2026-04-19 기준). breaking change 가능성을 고려해 `k-skill-rhwp` dependency 는 semver caret 으로 고정한다.\n- 이 스킬은 **편집 전용** 스킬이다. 조회/변환은 `hwp`, 고급 디버깅은 `rhwp-advanced` 가 담당한다.\n---","attachment_filenames":[],"attachments":[],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"rhwp-edit","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"What this skill does","type":"text"}]},{"type":"paragraph","content":[{"text":"k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI로 ","type":"text"},{"text":".hwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" 문서의 ","type":"text"},{"text":"본문 텍스트","type":"text","marks":[{"type":"strong"}]},{"text":", ","type":"text"},{"text":"표 구조","type":"text","marks":[{"type":"strong"}]},{"text":", ","type":"text"},{"text":"셀 내용","type":"text","marks":[{"type":"strong"}]},{"text":"을 round-trip 안전하게 수정한다. CLI는 ","type":"text"},{"text":"@rhwp/core","type":"text","marks":[{"type":"code_inline"}]},{"text":"(Rust + WebAssembly) 위에 얇은 Node 래퍼를 씌워 ","type":"text"},{"text":"insertText","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"deleteText","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"replaceAll","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"createTable","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"setCellText","type":"text","marks":[{"type":"code_inline"}]},{"text":" 같은 편집 동작을 서브커맨드로 노출한다. 결과는 항상 새 파일로 저장한다.","type":"text"}]},{"type":"paragraph","content":[{"text":"이 스킬은 ","type":"text"},{"text":"편집 전용","type":"text","marks":[{"type":"strong"}]},{"text":"이다. 문서를 Markdown/JSON으로 변환하거나 필드만 추출하려면 ","type":"text"},{"text":"hwp","type":"text","marks":[{"type":"link","attrs":{"href":"../hwp/SKILL.md","title":null}},{"type":"code_inline"}]},{"text":" 스킬을 사용한다. 페이지 렌더링 디버깅이나 IR 비교가 필요하면 ","type":"text"},{"text":"rhwp-advanced","type":"text","marks":[{"type":"link","attrs":{"href":"../rhwp-advanced/SKILL.md","title":null}},{"type":"code_inline"}]},{"text":" 스킬을 사용한다.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"HWP 본문에 한 줄 추가해줘\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"서식은 유지한 채로 2025를 2026으로 일괄 치환해줘\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"3행 4열짜리 표를 HWP에 넣어줘\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"표의 특정 셀 내용을 바꿔줘\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"빈 HWP 새 파일을 만들어줘\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When not to use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HWP → Markdown / JSON 변환","type":"text","marks":[{"type":"strong"}]},{"text":" → ","type":"text"},{"text":"hwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" 스킬(kordoc)을 쓴다. rhwp-edit은 바이너리 편집 전용이다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HWPX 원본을 다시 HWPX로 저장","type":"text","marks":[{"type":"strong"}]},{"text":" → rhwp v0.7.3 기준 업스트림이 ","type":"text"},{"text":"#196","type":"text","marks":[{"type":"code_inline"}]},{"text":"으로 HWPX 저장 경로를 막아둔 상태다. HWPX를 입력으로 주면 내부적으로 HWP IR로 올라온 뒤 ","type":"text"},{"text":"HWP 5.x 바이너리로만","type":"text","marks":[{"type":"strong"}]},{"text":" 저장된다. HWPX 출력이 꼭 필요하면 kordoc ","type":"text"},{"text":"markdownToHwpx","type":"text","marks":[{"type":"code_inline"}]},{"text":"를 쓴다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"레이아웃(페이지네이션·SVG 렌더) 디버깅","type":"text","marks":[{"type":"strong"}]},{"text":" → ","type":"text"},{"text":"rhwp-advanced","type":"text","marks":[{"type":"code_inline"}]},{"text":" 스킬로 업스트림 ","type":"text"},{"text":"rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI(","type":"text"},{"text":"export-svg --debug-overlay","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"dump-pages","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ir-diff","type":"text","marks":[{"type":"code_inline"}]},{"text":")를 사용한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"배포용(읽기전용) 잠금 해제 · IR 구조 덤프 · 썸네일 추출 등 고급 검사 명령","type":"text","marks":[{"type":"strong"}]},{"text":" → ","type":"text"},{"text":"rhwp-advanced","type":"text","marks":[{"type":"code_inline"}]},{"text":" 스킬 참조.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"한컴 오피스 GUI 자동화, 보안모듈 통과, Windows 전용 서식","type":"text","marks":[{"type":"strong"}]},{"text":" → 범위 밖이다. ","type":"text"},{"text":"rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":"는 파일 포맷 엔진이지 GUI 제어가 아니다.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Node.js 18+","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"쓰기 권한이 있는 출력 경로","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" 설치(셋 중 하나):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"일회성: ","type":"text"},{"text":"npx --yes k-skill-rhwp --help","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"전역: ","type":"text"},{"text":"npm install -g k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"로컬: ","type":"text"},{"text":"npm install k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":"는 ","type":"text"},{"text":"@rhwp/core@^0.7.3","type":"text","marks":[{"type":"code_inline"}]},{"text":"을 peer 없이 dependency로 끌어온다. 별도 설치 불필요.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rust/Cargo toolchain 불필요. 업스트림 ","type":"text"},{"text":"rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI를 같이 쓰고 싶으면 ","type":"text"},{"text":"rhwp-advanced","type":"text","marks":[{"type":"code_inline"}]},{"text":" 스킬로.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Inputs","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"입력 HWP / HWPX 경로 (절대 또는 상대)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"출력 HWP 경로 (항상 별도 파일. 원본을 덮어쓰지 않는다.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"편집 좌표: ","type":"text"},{"text":"--section N --paragraph N --offset N","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"표 좌표: ","type":"text"},{"text":"--section N --parent-paragraph N --control N --cell N [--cell-paragraph N]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"텍스트/쿼리: ","type":"text"},{"text":"--text \"...\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--query \"...\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--replacement \"...\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"create-table","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"--rows N --cols N","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"선택 플래그: ","type":"text"},{"text":"--case-sensitive","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--no-replace","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"set-cell-text","type":"text","marks":[{"type":"code_inline"}]},{"text":" 에서 기존 셀 내용 보존), ","type":"text"},{"text":"--format svg|html","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"render","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Routing policy","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"작업","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"기본 경로","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"본문 문단에 텍스트 삽입","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp insert-text","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"본문 문단에서 텍스트 삭제","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp delete-text","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"단순 전체 치환(같은 서식 유지, ","type":"text"},{"text":"본문 문단만","type":"text","marks":[{"type":"strong"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp replace-all --query ... --replacement ...","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"치환 대상 위치 사전 조회(","type":"text"},{"text":"본문 문단만","type":"text","marks":[{"type":"strong"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp search --query ... --from-section N --from-paragraph N","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"표 셀 안의 텍스트 확인","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp list-paragraphs","type":"text","marks":[{"type":"code_inline"}]},{"text":" + 셀 좌표 확인 후 ","type":"text"},{"text":"k-skill-rhwp set-cell-text","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 직접 쓰기","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"빈 표 삽입","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp create-table --rows N --cols N","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"표 셀 내용 교체/채우기","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp set-cell-text --control N --cell N --text \"...\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"빈 HWP 생성","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp create-blank \u003coutput.hwp>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"구조 파악(섹션/문단 수·길이)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp info \u003cfile>","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"list-paragraphs","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"페이지 SVG/HTML 미리보기","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp render \u003cfile> --page N --format svg","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"모든 편집 서브커맨드는 결과를 JSON 한 줄(CLI에서는 pretty-print)로 돌려준다. ","type":"text"},{"text":"ok: true","type":"text","marks":[{"type":"code_inline"}]},{"text":", 새 커서 위치(","type":"text"},{"text":"charOffset","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"paraIdx","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"controlIdx","type":"text","marks":[{"type":"code_inline"}]},{"text":"), 저장된 바이트 수(","type":"text"},{"text":"bytesWritten","type":"text","marks":[{"type":"code_inline"}]},{"text":"), 출력 경로(","type":"text"},{"text":"outputPath","type":"text","marks":[{"type":"code_inline"}]},{"text":") 를 포함한다.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"입력 점검","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"k-skill-rhwp info \u003cinput>","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 ","type":"text"},{"text":"sourceFormat","type":"text","marks":[{"type":"code_inline"}]},{"text":"(hwp/hwpx), ","type":"text"},{"text":"sectionCount","type":"text","marks":[{"type":"code_inline"}]},{"text":", 섹션별 ","type":"text"},{"text":"paragraphCount","type":"text","marks":[{"type":"code_inline"}]},{"text":", 문단별 ","type":"text"},{"text":"length","type":"text","marks":[{"type":"code_inline"}]},{"text":" 를 먼저 확인한다. 편집 좌표는 이 결과에서 뽑는다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"검색이 필요한 경우","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"k-skill-rhwp search \u003cinput> --query \"2025\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 섹션/문단/문자 오프셋을 먼저 얻고, 편집 명령에 그대로 넣는다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"편집","type":"text","marks":[{"type":"strong"}]},{"text":": 아래 예시 중 해당하는 서브커맨드 하나로 실행한다. ","type":"text"},{"text":"--output","type":"text","marks":[{"type":"code_inline"}]},{"text":" 은 항상 원본과 다른 경로를 지정한다.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 빈 문서 만들기\nnpx k-skill-rhwp create-blank ./out/blank.hwp\n\n# 본문 첫 문단 앞에 제목 삽입\nnpx k-skill-rhwp insert-text ./in.hwp ./out/with-title.hwp \\\n --section 0 --paragraph 0 --offset 0 \\\n --text \"2026년 오픈소스 AI·SW 지원사업 신청서\"\n\n# 2025 → 2026 일괄 치환\nnpx k-skill-rhwp replace-all ./in.hwp ./out/2026.hwp \\\n --query 2025 --replacement 2026\n\n# 3행 4열 표 삽입(본문 2번째 문단 끝)\nnpx k-skill-rhwp create-table ./in.hwp ./out/with-table.hwp \\\n --section 0 --paragraph 1 --offset 0 --rows 3 --cols 4\n\n# 방금 만든 표의 (0,0) 셀에 \"합계\" 삽입\n# - create-table 결과의 paraIdx / controlIdx 를 그대로 재사용\nnpx k-skill-rhwp set-cell-text ./out/with-table.hwp ./out/with-cell.hwp \\\n --section 0 --parent-paragraph \u003cparaIdx> --control \u003ccontrolIdx> \\\n --cell 0 --text \"합계\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"round-trip 검증","type":"text","marks":[{"type":"strong"}]},{"text":": 편집 직후 ","type":"text"},{"text":"k-skill-rhwp info \u003coutput>","type":"text","marks":[{"type":"code_inline"}]},{"text":" 를 다시 호출하고, 기대한 ","type":"text"},{"text":"paragraphs[].length","type":"text","marks":[{"type":"code_inline"}]},{"text":" 또는 ","type":"text"},{"text":"paragraphCount","type":"text","marks":[{"type":"code_inline"}]},{"text":" 변화를 직접 눈으로 확인한다. 필요하면 ","type":"text"},{"text":"k-skill-rhwp render \u003coutput> --page 0 --format html","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 첫 페이지 렌더 문자열이 생성되는지 sanity check 한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"민감 원본 보호","type":"text","marks":[{"type":"strong"}]},{"text":": 편집 대상이 개인정보/사업 신청서 등 비공개 문서라면 생성 파일을 레포에 커밋하지 않고, 로그에 남길 때도 본문을 요약·마스킹한다.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Node API (선택)","type":"text"}]},{"type":"paragraph","content":[{"text":"CLI가 아니라 Node 코드에서 직접 편집하고 싶으면 같은 패키지를 라이브러리로 쓴다.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"js"},"content":[{"text":"const { insertText, getDocumentInfo } = require(\"k-skill-rhwp\");\n\nawait insertText({\n input: \"./in.hwp\",\n output: \"./out.hwp\",\n section: 0,\n paragraph: 0,\n offset: 0,\n text: \"안녕하세요\"\n});\nconsole.log(await getDocumentInfo(\"./out.hwp\"));","type":"text"}]},{"type":"paragraph","content":[{"text":"Node 18+, ","type":"text"},{"text":"@rhwp/core","type":"text","marks":[{"type":"code_inline"}]},{"text":" WASM 은 첫 호출 시 한 번만 초기화된다. WASM 이 요구하는 ","type":"text"},{"text":"globalThis.measureTextWidth","type":"text","marks":[{"type":"code_inline"}]},{"text":" 콜백은 자동 shim 되므로 별도 설정 없이 돌아간다(정밀 레이아웃이 필요하면 ","type":"text"},{"text":"node-canvas","type":"text","marks":[{"type":"code_inline"}]},{"text":" 기반 shim을 먼저 주입한다).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Verify outputs after every run","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ok === true","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"bytesWritten","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 수 KB 이상.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"info","type":"text","marks":[{"type":"code_inline"}]},{"text":" 재호출 결과에서 섹션/문단 수·길이 변화가 의도와 일치.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"표 삽입의 경우 ","type":"text"},{"text":"paraIdx","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"controlIdx","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 다음 ","type":"text"},{"text":"set-cell-text","type":"text","marks":[{"type":"code_inline"}]},{"text":" 호출에 그대로 들어간다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"출력 파일이 원본과 다른 경로이며 원본은 그대로다.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Done when","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"사용자가 요청한 편집이 HWP 바이너리에 반영되어 새 파일로 저장됐다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"k-skill-rhwp info \u003coutput>","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 같은 혹은 늘어난 ","type":"text"},{"text":"sectionCount","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"paragraphCount","type":"text","marks":[{"type":"code_inline"}]},{"text":" 와 기대 ","type":"text"},{"text":"length","type":"text","marks":[{"type":"code_inline"}]},{"text":" 를 돌려준다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"원본 파일은 건드리지 않았다.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Failure modes","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HWPX 원본 저장 불가(rhwp #196)","type":"text","marks":[{"type":"strong"}]},{"text":": HWPX → HWPX round-trip 은 upstream에서 비활성화 상태다. HWPX 입력이라도 출력은 HWP로만 저장된다. 원본 확장자에 의존하지 말고 항상 ","type":"text"},{"text":".hwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 저장한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"좌표 범위 초과","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"section/paragraph/offset","type":"text","marks":[{"type":"code_inline"}]},{"text":" 이 실제 문서 범위를 벗어나면 WASM에서 ","type":"text"},{"text":"렌더링 오류: 구역 인덱스 0 범위 초과","type":"text","marks":[{"type":"code_inline"}]},{"text":" 같은 에러를 던지고 CLI는 exit code 1 + stderr 에 메시지를 찍는다. 편집 전에 ","type":"text"},{"text":"info","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 좌표를 확인한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"복잡한 표·이미지·양식 필드 round-trip","type":"text","marks":[{"type":"strong"}]},{"text":": 현재 업스트림 rhwp v0.7.x 는 베타다. 복잡한 표·이미지·차트·양식필드가 많은 실제 사업 신청서를 HWP round-trip 할 경우 드물게 형식 손실이 발생할 수 있다. round-trip 이 끝나면 ","type":"text"},{"text":"k-skill-rhwp render \u003coutput>","type":"text","marks":[{"type":"code_inline"}]},{"text":" + 육안 확인을 권장한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"배포용(읽기전용) 문서","type":"text","marks":[{"type":"strong"}]},{"text":": rhwp 자체는 ","type":"text"},{"text":"convertToEditable","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 잠금 해제를 지원하지만 ","type":"text"},{"text":"k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI 서브커맨드는 아직 노출하지 않는다. 필요하면 ","type":"text"},{"text":"rhwp-advanced","type":"text","marks":[{"type":"code_inline"}]},{"text":" 스킬의 업스트림 ","type":"text"},{"text":"rhwp convert","type":"text","marks":[{"type":"code_inline"}]},{"text":" 경로를 쓴다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"WASM 초기화","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"@rhwp/core","type":"text","marks":[{"type":"code_inline"}]},{"text":" 번들 WASM(~4 MB) 은 최초 호출 시 한 번 파싱한다. 첫 호출은 수십 ms~수백 ms 지연될 수 있다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"파일 인코딩","type":"text","marks":[{"type":"strong"}]},{"text":": 한국어 텍스트는 UTF-8 로 그대로 CLI 에 넘기면 된다. 셸에서 인용부호가 깨질 경우 ","type":"text"},{"text":"--text=

rhwp-edit What this skill does CLI로 문서의 본문 텍스트 , 표 구조 , 셀 내용 을 round-trip 안전하게 수정한다. CLI는 (Rust + WebAssembly) 위에 얇은 Node 래퍼를 씌워 , , , , 같은 편집 동작을 서브커맨드로 노출한다. 결과는 항상 새 파일로 저장한다. 이 스킬은 편집 전용 이다. 문서를 Markdown/JSON으로 변환하거나 필드만 추출하려면 스킬을 사용한다. 페이지 렌더링 디버깅이나 IR 비교가 필요하면 스킬을 사용한다. When to use - "HWP 본문에 한 줄 추가해줘" - "서식은 유지한 채로 2025를 2026으로 일괄 치환해줘" - "3행 4열짜리 표를 HWP에 넣어줘" - "표의 특정 셀 내용을 바꿔줘" - "빈 HWP 새 파일을 만들어줘" When not to use - HWP → Markdown / JSON 변환 → 스킬(kordoc)을 쓴다. rhwp-edit은 바이너리 편집 전용이다. - HWPX 원본을 다시 HWPX로 저장 → rhwp v0.7.3 기준 업스트림이 으로 HWPX 저장 경로를 막아둔 상태다. HWPX를 입력으로 주면 내부적으로 HWP IR로 올라온…

...'","type":"text","marks":[{"type":"code_inline"}]},{"text":" 같은 형식을 쓴다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"search","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" / ","type":"text","marks":[{"type":"strong"}]},{"text":"replace-all","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" 은 본문 문단만 스캔한다","type":"text","marks":[{"type":"strong"}]},{"text":": 업스트림 ","type":"text"},{"text":"searchText","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 본문(body) 범위로 제한되어 있고, ","type":"text"},{"text":"k-skill-rhwp replace-all","type":"text","marks":[{"type":"code_inline"}]},{"text":" 도 같은 스코프를 그대로 따른다. ","type":"text"},{"text":"표(cell) 안의 텍스트, 머리말/꼬리말, 각주 본문","type":"text","marks":[{"type":"strong"}]},{"text":"에서는 ","type":"text"},{"text":"search","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 ","type":"text"},{"text":"found:false","type":"text","marks":[{"type":"code_inline"}]},{"text":" 를 돌려주고 ","type":"text"},{"text":"replace-all","type":"text","marks":[{"type":"code_inline"}]},{"text":" 도 해당 위치를 건드리지 않는다. 셀 내용이 대상이라면 ","type":"text"},{"text":"list-paragraphs","type":"text","marks":[{"type":"code_inline"}]},{"text":" 또는 ","type":"text"},{"text":"info","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 표 좌표를 잡고 ","type":"text"},{"text":"set-cell-text","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 직접 쓴다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"문단 경계 / 개행 치환 금지","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"replace-all","type":"text","marks":[{"type":"code_inline"}]},{"text":" 은 한 문단 안에서의 치환만 보장한다. ","type":"text"},{"text":"--replacement","type":"text","marks":[{"type":"code_inline"}]},{"text":" 에 개행(","type":"text"},{"text":"\\n","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\\r","type":"text","marks":[{"type":"code_inline"}]},{"text":", U+2028, U+2029) 이 들어오면 CLI 는 exit code 1 과 \"replacement must not contain newline or paragraph-break characters\" 메시지를 돌려준다. 여러 문단을 만들고 싶으면 ","type":"text"},{"text":"insert-text","type":"text","marks":[{"type":"code_inline"}]},{"text":" 를 여러 번 호출한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"치환은 원본 매칭 기준 non-overlapping","type":"text","marks":[{"type":"strong"}]},{"text":": 예를 들어 query ","type":"text"},{"text":"a","type":"text","marks":[{"type":"code_inline"}]},{"text":" / replacement ","type":"text"},{"text":"aa","type":"text","marks":[{"type":"code_inline"}]},{"text":" / 원본 ","type":"text"},{"text":"aaa","type":"text","marks":[{"type":"code_inline"}]},{"text":" 는 원본의 각 ","type":"text"},{"text":"a","type":"text","marks":[{"type":"code_inline"}]},{"text":" 를 한 번씩 교체해 ","type":"text"},{"text":"aaaaaa","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 된다. 치환으로 새로 들어온 문자열은 다시 매칭하지 않는다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"대소문자 무시 매칭은 UTF-16 길이가 보존되는 문자에만 안전하다","type":"text","marks":[{"type":"strong"}]},{"text":": 기본값인 대소문자 무시(","type":"text"},{"text":"--case-sensitive","type":"text","marks":[{"type":"code_inline"}]},{"text":" 없이) 모드는 ","type":"text"},{"text":"String.prototype.toLowerCase()","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 UTF-16 길이를 그대로 유지한다는 전제 위에서 오프셋을 계산한다. 터키어 ","type":"text"},{"text":"İ","type":"text","marks":[{"type":"code_inline"}]},{"text":"(U+0130) 처럼 소문자화 시 ","type":"text"},{"text":"i","type":"text","marks":[{"type":"code_inline"}]},{"text":" + 결합 점(U+0307) 로 길이가 늘어나는 문자가 본문 또는 쿼리에 포함되면, 조용한 문서 손상을 방지하기 위해 ","type":"text"},{"text":"replace-all","type":"text","marks":[{"type":"code_inline"}]},{"text":" 이 exit code 1 과 함께 ","type":"text"},{"text":"case-insensitive matching is unsafe because case folding changes the UTF-16 length","type":"text","marks":[{"type":"code_inline"}]},{"text":" 메시지를 돌려준다. 이런 문서에는 ","type":"text"},{"text":"--case-sensitive","type":"text","marks":[{"type":"code_inline"}]},{"text":" 로 다시 실행하거나, 입력을 미리 정규화한다. 한글·ASCII 본문에는 해당하지 않으며, ","type":"text"},{"text":"2025 → 2026","type":"text","marks":[{"type":"code_inline"}]},{"text":" 같은 실제 사업 신청서 워크플로우는 아무 영향을 받지 않는다.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Notes","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"업스트림 rhwp: https://github.com/edwardkim/rhwp","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"업스트림 ","type":"text"},{"text":"@rhwp/core","type":"text","marks":[{"type":"code_inline"}]},{"text":" npm: https://www.npmjs.com/package/@rhwp/core","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"업스트림은 활발히 개발 중이다(v0.7.3 2026-04-19 기준). breaking change 가능성을 고려해 ","type":"text"},{"text":"k-skill-rhwp","type":"text","marks":[{"type":"code_inline"}]},{"text":" dependency 는 semver caret 으로 고정한다.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"이 스킬은 ","type":"text"},{"text":"편집 전용","type":"text","marks":[{"type":"strong"}]},{"text":" 스킬이다. 조회/변환은 ","type":"text"},{"text":"hwp","type":"text","marks":[{"type":"code_inline"}]},{"text":", 고급 디버깅은 ","type":"text"},{"text":"rhwp-advanced","type":"text","marks":[{"type":"code_inline"}]},{"text":" 가 담당한다.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"rhwp-edit","author":"@skillopedia","source":{"stars":5322,"repo_name":"k-skill","origin_url":"https://github.com/nomadamas/k-skill/blob/HEAD/rhwp-edit/SKILL.md","repo_owner":"nomadamas","body_sha256":"49a4fae0ee34e88216b1011d175b08953ed1d69cc7af59b3e9d0e3f86dbc317f","cluster_key":"c733576dba38585c001eb1233e19413604a6b26ac34f4ea65c822a981dfa9bb2","clean_bundle":{"format":"clean-skill-bundle-v1","source":"nomadamas/k-skill/rhwp-edit/SKILL.md","bundle_sha256":"f3bca3f6c211891bf8d3a54eb94118b1a1411fdc125183d67373319939c65e0e","attachment_count":0,"text_attachments":0,"binary_attachments":0},"cluster_size":1,"skill_md_path":"rhwp-edit/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"documents-office","category_label":"Documents"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"documents-office","metadata":{"phase":"v1.5","locale":"ko-KR","category":"documents"},"import_tag":"clean-skills-v1","description":"Edit HWP documents — insert/delete text, replace-all, create tables, set cell text — with the k-skill-rhwp CLI that wraps the @rhwp/core WASM engine (rhwp by Edward Kim)."}},"renderedAt":1782979909856}

rhwp-edit What this skill does CLI로 문서의 본문 텍스트 , 표 구조 , 셀 내용 을 round-trip 안전하게 수정한다. CLI는 (Rust + WebAssembly) 위에 얇은 Node 래퍼를 씌워 , , , , 같은 편집 동작을 서브커맨드로 노출한다. 결과는 항상 새 파일로 저장한다. 이 스킬은 편집 전용 이다. 문서를 Markdown/JSON으로 변환하거나 필드만 추출하려면 스킬을 사용한다. 페이지 렌더링 디버깅이나 IR 비교가 필요하면 스킬을 사용한다. When to use - "HWP 본문에 한 줄 추가해줘" - "서식은 유지한 채로 2025를 2026으로 일괄 치환해줘" - "3행 4열짜리 표를 HWP에 넣어줘" - "표의 특정 셀 내용을 바꿔줘" - "빈 HWP 새 파일을 만들어줘" When not to use - HWP → Markdown / JSON 변환 → 스킬(kordoc)을 쓴다. rhwp-edit은 바이너리 편집 전용이다. - HWPX 원본을 다시 HWPX로 저장 → rhwp v0.7.3 기준 업스트림이 으로 HWPX 저장 경로를 막아둔 상태다. HWPX를 입력으로 주면 내부적으로 HWP IR로 올라온…