Imported from ai-screams/HwpForge (
.claude/skills/hwpforge/SKILL.md). Install upstream withnpx skills add ai-screams/HwpForge --skill hwpforge. Copyright stays with the author (MIT).
HwpForge Skill
Overview
HwpForge is a CLI (hwpforge) for the Korean HWPX document format (KS X 6101) used in
government proposals, official reports, and administrative documents. It can:
- Create HWPX from Markdown (with Korean style presets)
- Convert legacy HWP5 (
.hwp) → HWPX, and HWPX → Markdown - Edit existing HWPX via a JSON round-trip (fill placeholders, fix text, add content)
- Inspect structure and emit JSON Schemas
Every command accepts --json for machine-readable output and structured error codes.
There is no
.hwp(HWP5) writer. HwpForge reads.hwpbut only writes.hwpx. To work with a.hwp, convert it to.hwpxfirst (convert-hwp5).
The Algorithm — pick the right command
Follow this decision flow. Choosing the wrong path is the most common mistake.
What does the user want?
│
├─ Create a NEW document from text / Markdown
│ → convert (Markdown → HWPX, with --preset)
│
├─ They have a legacy .hwp file
│ → convert-hwp5 (.hwp → .hwpx) then treat it as HWPX
│
├─ EDIT an existing .hwpx ── ALWAYS `outline` first (nav map), `read` to see targets ──
│ │
│ ├─ Fill NAMED click-here fields (누름틀) — form-style templates
│ │ → fields (discover names) → fill --set name=value [DELTA, cheapest+safest]
│ │
│ ├─ Template has NO 누름틀, only prose placeholders (□, ( ), 년 월 일, (인), @)
│ │ → stamp-plan (discover) → author spec map → stamp --map [STAMP, one-time]
│ │ then the stamped output is a form template: use fields/fill above
│ │
│ ├─ Fill a TABLE CELL by position or label (병합셀 표 서식)
│ │ → to-json (cells carry addr {row,col}) → set-cell [GRID, admission-gated]
│ │ --table N --at "r,c" | --right-of LABEL | --below LABEL, --text "" clears
│ │ covered coords resolve to their merge anchor (reported in the result)
│ │
│ ├─ Change only EXISTING text
│ │ (fill a prose placeholder, fix a typo, fill a table cell)
│ │ → to-json (--section) → edit the Text → patch [TEXT-ONLY, safest]
│ │
│ ├─ ADD or REMOVE a top-level paragraph (structural edit, byte-preserving)
│ │ → outline/read to find the index → insert-para / delete-para [E4]
│ │ insert-para --section N --anchor I [--before] --text "…" (shape inherited;
│ │ --text 반복 = 연속 블록 batch 삽입)
│ │ delete-para --section N --index I [--index J …] (batch)
│ │ IndexMark 든 문단 삭제 시 warnings 로 색인 소멸 advisory (거부 아님)
│ │ fail-closed: refuses deleting a paragraph with a reference
│ │ (bookmark/cross-ref/footnote), a hard page/column break, or the
│ │ section's first paragraph (secPr). Only round-trip-safe inputs.
│ │ verify with `diff base out`. (표 행 추가/삭제는 아직 미지원)
│ │
│ └─ REBUILD from scratch (structural change beyond paragraphs)
│ → to-json (full) → edit → from-json --base [REBUILD]
│ ⚠ 표 구조를 바꿨다면(행/열/셀 추가·삭제) 셀의 addr 필드를 삭제하고
│ 제출할 것 — 남겨두면 stale 주소로 GRID_ADDR_INVALID 거부됨
│ (addr 부재 = 무검사, 존재 = 재파생 격자와 대조)
│
├─ Read / export an existing .hwpx — pick by granularity:
│ → outline (nav map: headings/tables+dims/fields/bookmarks — fetch ONCE first)
│ → read (targeted text: --section N [--paras A..B] | --table N | --field NAME)
│ → inspect (counts summary only)
│ → to-md (full lossy flatten, for human reading)
│ → to-json (whole document JSON, for machine editing ONLY)
│
├─ Render to PDF (한컴과 같은 출력 — layout-cache replay)
│ → to-pdf (needs a 한컴-saved document: cacheless files are rejected)
│
└─ Need the JSON shape, or the list of styles
→ schema (JSON Schema for document/section types)
→ templates list (available style presets)
The two edit modes are not interchangeable:
| Mode | Command | Can do | Cannot do |
|---|---|---|---|
| Text-only | patch |
change text inside existing paragraphs and table cells; preserves images, styles, tables, layout exactly | add/remove paragraphs (returns PATCH_FAILED: structural change detected) |
| Rebuild | from-json --base |
add/remove paragraphs, structural edits; preserves tables; --base inherits images |
guarantee byte-perfect fidelity of complex 한컴 forms (see Fidelity Warning) |
Commands
Run hwpforge <command> --help for exact flags. Key forms:
# Create (convert --preset currently accepts only `default`; see Presets)
hwpforge convert input.md -o out.hwpx [--preset default]
echo "# 제목" | hwpforge convert - -o out.hwpx # stdin via "-"
# Legacy HWP5
hwpforge convert-hwp5 old.hwp -o out.hwpx
# --carry-layout-cache : carry the HWP5 layout cache so `to-pdf` can render
# the output (PDF replay/comparison ONLY — not for Hancom re-open)
# Navigation map (ALWAYS before editing) + targeted reads (E5)
hwpforge outline doc.hwpx [--json] # headings / tables(ordinal+dims) / fields / bookmarks
hwpforge read doc.hwpx --section 0 --paras 2..5 # paragraph text with kinds + content markers
hwpforge read doc.hwpx --table 3 # grid text matrix (merged cells appear at anchor)
hwpforge read doc.hwpx --field 과제명 # one named field
# name anchors (heading text, table ordinal, field name) are primary keys;
# {section, para} locators go stale after structural edits
# Verify AFTER every edit (E5) — did ONLY the intended delta land?
hwpforge diff base.hwpx edited.hwpx [--json] [-o report.json]
# semantic = field values / cell {table,row,col} / paragraph {section,para} / structure / raw
# package = ZIP entries by bytes; entry-internal layout caches are NOT itemized (report says so)
# Inspect (counts summary)
hwpforge inspect doc.hwpx [--styles] [--json]
# Named click-here fields (누름틀) — form templates: discover then fill
hwpforge fields doc.hwpx [--json] # list name/hint/current/fillable
hwpforge fill doc.hwpx --set 과제명="AI 문서 자동화" --set 기관명="AiScream" -o out.hwpx
# all-or-nothing: 하나라도 검증 실패(없는 이름/중복 이름/빈 값/모호 필드)면 아무 것도 안 씀
# 나머지 패키지 엔트리는 바이트 그대로 보존 (preserve-first)
# Template stamping (E6) — promote prose placeholders (□, ( ), 년 월 일, (인), @)
# AND label-adjacent empty table cells (클래스-B) to named 누름틀 so fields/fill work.
# One-time preprocessing per template.
hwpforge stamp-plan template.hwpx --json # candidates(text) + cells + source_sha256
# → author a spec map: EVERY unguarded candidate gets {"action":{"field":{"name":"…"}}}
# or {"action":"ignore"}; guarded ones (※/【작성방법】/(예시) context) may be omitted
# text-only 는 legacy 배열 맵 그대로; 셀이 있으면 v2 객체 맵:
# {"schema_version":2, "source_sha256":"<plan 값 그대로>",
# "text":[…], "cells":[{"table":0,"at":{"row":5,"col":3},
# "label":{"at":{"row":5,"col":2},"text":"성 명"}, ← detected 후보 드리프트 재검증 (orphan 명시 스펙은 생략)
# "action":{"field":{"name":"성명","hint":"성명 입력"}}}]} ← 셀 hint 는 필수 (빈 셀엔 마커가 없음)
# suggested_name/suggested_hint 는 제안일 뿐 — 그대로 복사 시 중복 이름은 거부됨
hwpforge stamp template.hwpx --map specs.json -o form.hwpx # + form.manifest.json (v2: origin text|cell)
# fail-closed: 무손실 왕복이 증명 안 되는 입력은 거부(INPUT_NOT_ROUNDTRIP_SAFE);
# 무가드 후보(양쪽 클래스) 누락 거부(STAMP_CANDIDATE_UNCOVERED); 문서 변경 후 재사용
# 맵은 거부(STAMP_SOURCE_HASH_MISMATCH — stamp-plan 재실행); 셀 스펙은 병합 앵커 좌표만
# (STAMP_CELL_NOT_ANCHOR 가 앵커를 알려줌). 이후 fields/fill 로 채움
# Structural paragraph editing (E4) — insert/delete top-level paragraphs, byte-preserving.
hwpforge insert-para doc.hwpx --section 0 --anchor 3 --text "추가 문단" -o out.hwpx
hwpforge insert-para doc.hwpx --section 0 --anchor 3 --before --text "앞에 추가" -o out.hwpx
hwpforge insert-para doc.hwpx --section 0 --anchor 3 --text "하나" --text "둘" -o out.hwpx # 연속 블록 batch
hwpforge delete-para doc.hwpx --section 0 --index 5 -o out.hwpx # batch: --index 5 --index 7
# 새 문단은 앵커의 문단/글자 모양을 상속(스타일 발명 없음, batch 는 균일 상속); --text 는 한 줄 평문
# delete 는 IndexMark 문단에 warnings advisory 를 내보냄 (JSON: warnings 배열, 텍스트: stderr)
# fail-closed 거부: 참조(책갈피/상호참조/각주) 든 문단 삭제·hard page/column break·
# 섹션 첫 문단(secPr)·섹션을 비우는 삭제. round-trip-safe 입력만. 표 행 편집은 미지원.
# 반드시 `diff base out` 로 의도한 델타만 났는지 검증
# Grid cell editing (E3) — fill table cells by logical grid address.
# to-json export annotates every cell with addr {row,col} (병합 전 논리 격자).
hwpforge set-cell form.hwpx --table 0 --at "1,2" --text "홍길동" -o out.hwpx
hwpforge set-cell form.hwpx --table 0 --right-of "성명" --text "홍길동" -o out.hwpx
hwpforge set-cell form.hwpx --table 0 --below "비고" --text "" -o out.hwpx # "" = clear
hwpforge set-cell form.hwpx --map cells.json -o out.hwpx # batch: [{"table":0,"at":{"row":1,"col":2},"text":"…"}]
# 피병합 좌표는 병합 앵커로 resolve (결과에 requested/anchor/resolution 명시);
# 라벨은 NFC+공백 정규화 exact match (모호하면 CELL_LABEL_AMBIGUOUS — --at 로 지정);
# 표/이미지/컨트롤 든 셀은 거부(CELL_HAS_NON_TEXT_CONTENT); stamp 와 같은 admission 게이트
# Export (NOTE: -o/--output is REQUIRED — there is no stdout export)
hwpforge to-json doc.hwpx -o full.json # whole document
hwpforge to-json doc.hwpx --section 0 -o sec.json # one section
hwpforge to-json doc.hwpx --section 0 --no-styles -o sec.json
# Write back
hwpforge patch doc.hwpx --section 0 sec.json -o doc.hwpx # text-only
hwpforge from-json full.json -o doc.hwpx --base doc.hwpx # rebuild (inherit images)
# Render to PDF (layout-cache replay — 한컴 재저장본만; format detected by content)
hwpforge to-pdf doc.hwpx [-o out.pdf] [--font-dir DIR] [--discovery explicit|hancom|platform] \
[--degraded] [--partial-cache-reject] [--json]
# macOS + 한컴오피스 설치 시: --discovery hancom 으로 번들 폰트 자동 발견.
# 실무 문서는 언어축 혼합이 흔해 --degraded 권장 (경고로 표면화됨).
# 실패는 fail-closed: cacheless → 한컴에서 열어 재저장 후 재시도.
# Read out / schema / styles
hwpforge to-md doc.hwpx -o doc.md
hwpforge schema [document|exported-document|exported-section]
hwpforge templates list [--json]
hwpforge templates show default
Diagnostic (parity/QA, not for normal authoring): audit-hwp5, census-hwp5.
Presets
templates list catalogs four: default (함초롬돋움 10pt), modern (맑은 고딕),
classic (바탕), latest (함초롬바탕) — all A4. However, convert --preset currently
resolves only default (others return UNKNOWN_PRESET). Use default for convert; the
catalog entries are inspectable via hwpforge templates show <name>. See
templates.md.
Editing an existing document (JSON round-trip)
The exported section/document JSON is structure + style references (IDs). Full recipes: editing-workflow.md. Filling a Korean template (e.g. 국가과제 제안서): template-fill.md.
Minimal text-only edit (fill placeholders, fix text, fill table cells):
hwpforge inspect doc.hwpx --json # 1. understand structure
hwpforge to-json doc.hwpx --section 0 -o sec.json # 2. export section
# 3. edit runs[].content.Text (and table cell text) in sec.json — keep style IDs as-is
hwpforge patch doc.hwpx --section 0 sec.json -o doc.hwpx # 4. write back (text-only)
hwpforge inspect doc.hwpx # 5. verify
Add new paragraphs (structural → rebuild):
hwpforge to-json doc.hwpx -o full.json # full document
# append paragraph objects to document.sections[N].paragraphs
# reuse a neighboring paragraph's para_shape_id + char_shape_id (do NOT invent IDs)
hwpforge from-json full.json -o doc.hwpx --base doc.hwpx # rebuild
hwpforge inspect doc.hwpx # paragraph count increased
JSON rules (these prevent broken output)
- Reuse existing style IDs. New paragraphs/runs must copy
para_shape_id/char_shape_idfrom a neighboring paragraph in the same document. Never invent IDs. style_idandheading_levelare optional per paragraph — copy them only if the source paragraph has them; omit otherwise.patchreplaces the whole section, sosec.jsonmust contain ALL existing paragraphs plus your edits — it is a read-modify-write of the full section, not a delta.- Do not edit the
stylesregistry by hand — change styles via--presetinstead. - Table cell text lives at
…content.Table.rows[].cells[].paragraphs[].runs[].content.Text.
Fidelity Warning (government / 한컴-authored templates)
patch (text-only) preserves the original file structure exactly — prefer it for real
한컴 templates (form fields, master pages, complex tables) where formatting is mandatory.
from-json --base rebuilds the document from HwpForge's internal model. Simple tables and
paragraphs survive, but elements HwpForge does not yet fully model (form controls, master pages,
some advanced formatting) can be lost. Never submit a rebuilt government document without
opening it in 한컴 and checking it visually. When in doubt, fill placeholders with patch.
Document Scenarios
| Scenario | File | Use When |
|---|---|---|
| 정부 제안서 (Government Proposal) | scenario-proposal.md | RFP response, project bid, tender |
| 보고서 (Report) | scenario-report.md | Research/progress report, analysis |
| 공문서 (Official Document) | scenario-official.md | Administrative correspondence, notice |
| 템플릿 채우기 (Template Fill) | template-fill.md | Fill an existing Korean template with content |
Korean Markdown Best Practices
See markdown-guide.md: GFM tables, YAML frontmatter
(title, author, date, preset), image paths, --- as page break, Korean characters.
Agent Behavior Rules
Output: No Raw JSON
Never show raw JSON to the user during round-trip workflows. Summarize as a table, structure diagram, or short description. Keep intermediate JSON in temp files for internal use only.
Edit: In-Place by Default
When the user asks to modify a specific file, overwrite the original unless they specify a
different output path — set -o to the input path.
hwpforge patch document.hwpx --section 0 modified.json -o document.hwpx # default: overwrite
Always outline before editing, always diff after
Run outline first to learn what is where (use read for the targets you will touch), and
diff base.hwpx edited.hwpx after every edit to confirm ONLY the intended delta landed
before reporting success — an unexpected entry in the diff report means stop and re-check,
not ship.
Error Handling
With --json, all commands return structured errors:
{ "error": { "code": "PATCH_FAILED", "message": "...", "hint": "..." } }
Common: FILE_NOT_FOUND (bad path), PATCH_FAILED with "structural change detected"
(you added/removed paragraphs in a patch — use from-json --base instead).
Exit codes: 1 user error (bad input/missing file), 2 internal error (encode/corrupt).
Use --json in all agent workflows to parse errors programmatically.