Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 131 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center">
<h1 align="center">📄 hwpx-skill</h1>
<p align="center">
<strong>한글(HWPX) 문서를 AI 에이전트가 안전하게 읽고·편집하고·자동화하는 프로덕션용 스킬</strong>
<strong>AI 에이전트가 HWPX 문서를 바로 읽고, 바꾸고, 점검하게 만드는 공식 온보딩 스킬</strong>
</p>
<p align="center">
순수 Python · 한컴오피스 불필요 · 크로스 플랫폼
Expand All @@ -25,9 +25,14 @@
| 🎯 에이전트 스킬 | **[`hwpx-skill`](https://github.com/airmang/hwpx-skill)** | 에이전트가 HWPX를 바로 쓰게 해주는 공식 온보딩 스킬 |

---
`hwpx-skill`은 `python-hwpx` 기반의 에이전트 스킬이다. `.hwpx` 문서를 열고, 텍스트를 추출하고, 표를 포함한 양식을 채우고, 플레이스홀더를 치환하는 작업을 에이전트가 바로 수행할 수 있게 설계했다.

이 레포의 차별점은 단순한 커뮤니티 래퍼가 아니라는 점이다. `python-hwpx` 라이브러리 저자가 직접 관리하는 공식 스킬이므로, 라이브러리 실제 API와 예제가 함께 유지된다.
`hwpx-skill`은 `python-hwpx` 기반의 공식 에이전트 스킬이다. HWPX를 잘 모르는 사용자도 **스킬 설치 후 바로 문서 읽기, 텍스트 추출, 템플릿 치환, 기본 점검**까지 갈 수 있게 만드는 데 초점을 둔다. `.hwpx` 문서를 열고, 텍스트를 추출하고, 표를 포함한 양식을 채우고, 플레이스홀더를 치환하는 작업을 에이전트가 바로 수행할 수 있게 설계했다.

즉, 이 저장소는 단순 설명서가 아니다.
- 에이전트가 따라갈 `SKILL.md`
- 바로 실행해볼 수 있는 예제
- 입문자가 첫 성공을 확인하는 보조 CLI
를 함께 제공하는 **HWPX 자동화 입구**다.

> 대상 포맷은 Open XML 기반 `.hwpx`다. 레거시 바이너리 `.hwp` 직접 편집은 범위 밖이다.

Expand All @@ -37,10 +42,74 @@
- **Cursor Skills / Rules** — `.cursor/skills/`와 `.cursor/rules/` 조합으로 온보딩할 수 있다.
- **Codex CLI Skills** — `.agents/skills/hwpx-skill/` 경로 기준으로 바로 붙일 수 있다.

## 이 저장소가 바로 해결하는 일

- HWPX 문서 텍스트를 빠르게 추출한다.
- 표를 포함한 문서의 플레이스홀더를 일괄 치환한다.
- 설치 직후 환경이 맞는지 한 번에 확인한다.
- 에이전트가 HWPX 작업에서 어떤 흐름을 따라야 하는지 알려준다.

## 3분 설치

기본 명령:

```bash
python3 -m pip install -U python-hwpx lxml
```

현재 권장 기준:
- Python 3.10+
- 최소 호환 기준: `python-hwpx >= 2.6`
- 최근 로컬 검증 기준: `python-hwpx 2.9.0`

## 5분 성공 확인

설치 후 이 명령 하나부터 돌린다.

```bash
python3 scripts/quickcheck.py
```

이 스크립트는 다음을 한 번에 확인한다.
- Python 버전
- `python-hwpx`, `lxml` import
- 예제 문서 생성
- 생성 문서 구조 점검
- CLI 텍스트 추출

정상이라면 마지막에 아래 문구가 나온다.

```text
[OK] basic hwpx skill workflow passed
```

## 가장 많이 쓰는 작업 3개

### 1) 문서 텍스트 바로 추출

```bash
python3 scripts/text_extract.py input.hwpx
python3 scripts/text_extract.py input.hwpx --format json --include-nested --out output.json
```

### 2) 플레이스홀더 전역 치환

```bash
python3 scripts/zip_replace_all.py template.hwpx output.hwpx --replace "{학교명}=테스트초" "{담당자}=홍길동" --auto-fix-ns
```

### 3) 예제 문서 생성 후 구조 확인

```bash
python3 examples/01_create_and_save.py
python3 examples/02_extract_and_inspect.py examples/out/01_created.hwpx
```

## 포함 내용

- `SKILL.md`: 에이전트용 의사결정 트리와 실전 워크플로
- `references/api.md`: `python-hwpx` API 레퍼런스
- `scripts/quickcheck.py`: 설치 직후 첫 성공 경로를 점검하는 CLI
- `scripts/text_extract.py`: 텍스트 추출 CLI
- `scripts/zip_replace_all.py`: 플레이스홀더 전역 치환 CLI
- `scripts/fix_namespaces.py`: ZIP-level 수정 후 namespace 정리
Expand All @@ -56,6 +125,7 @@ hwpx-skill/
│ └── api.md
├── scripts/
│ ├── fix_namespaces.py
│ ├── quickcheck.py
│ ├── text_extract.py
│ └── zip_replace_all.py
└── examples/
Expand All @@ -64,18 +134,6 @@ hwpx-skill/
└── 03_template_replace.py
```

## 공통 의존성

모든 플랫폼에서 먼저 Python 의존성을 설치한다.

```bash
python -m pip install -U python-hwpx lxml
```

현재 권장 기준:
- 최소 호환 기준: `python-hwpx >= 2.6`
- 최근 로컬 검증 기준: `python-hwpx 2.9.0`

## Claude Code 설치

프로젝트 로컬 설치:
Expand All @@ -96,7 +154,13 @@ python -m pip install -U python-hwpx lxml
2. 아래 명령으로 의존성을 설치한다.

```bash
python -m pip install -U python-hwpx lxml
python3 -m pip install -U python-hwpx lxml
```

3. 아래 명령으로 첫 성공 경로를 확인한다.

```bash
python3 scripts/quickcheck.py
```

에이전트가 `한글 문서 편집`, `가정통신문 작성`, `공문 양식 채우기`, `HWPX 플레이스홀더 치환` 같은 요청을 받으면 스킬이 트리거되도록 `SKILL.md` description을 유지한다.
Expand All @@ -118,7 +182,7 @@ python -m pip install -U python-hwpx lxml
의존성 설치:

```bash
python -m pip install -U python-hwpx lxml
python3 -m pip install -U python-hwpx lxml
```

권장 트리거 룰 파일:
Expand Down Expand Up @@ -159,47 +223,53 @@ Cursor에서 스킬과 룰을 함께 두면 자연어 요청과 파일 확장자
의존성 설치:

```bash
python -m pip install -U python-hwpx lxml
python3 -m pip install -U python-hwpx lxml
```

Codex CLI에서는 `SKILL.md` frontmatter의 `description`이 핵심 트리거 역할을 한다. 따라서 자연어 요청과 도메인 키워드를 충분히 담은 상태로 유지하는 것이 중요하다.

## 빠른 검증

설치 직후 최소 성공 경로는 이 셋이면 충분하다.
가장 빠른 검증은 `quickcheck.py`다.

```bash
python examples/01_create_and_save.py
python examples/02_extract_and_inspect.py examples/out/01_created.hwpx
python scripts/text_extract.py examples/out/01_created.hwpx
python3 scripts/quickcheck.py
```

수동으로 최소 성공 경로를 밟으려면 아래 셋이면 충분하다.

```bash
python3 examples/01_create_and_save.py
python3 examples/02_extract_and_inspect.py examples/out/01_created.hwpx
python3 scripts/text_extract.py examples/out/01_created.hwpx
```

플레이스홀더 치환까지 확인하려면:

```bash
python examples/03_template_replace.py examples/out/01_created.hwpx examples/out/03_replaced.hwpx --replace "학부모님께 안내드립니다.=학부모님께 수정 안내드립니다."
python examples/02_extract_and_inspect.py examples/out/03_replaced.hwpx
python3 examples/03_template_replace.py examples/out/01_created.hwpx examples/out/03_replaced.hwpx --replace "학부모님께 안내드립니다.=학부모님께 수정 안내드립니다."
python3 examples/02_extract_and_inspect.py examples/out/03_replaced.hwpx
```

## 빠른 사용 예시

텍스트 추출:

```bash
python scripts/text_extract.py input.hwpx
python scripts/text_extract.py input.hwpx --format json --include-nested --out output.json
python3 scripts/text_extract.py input.hwpx
python3 scripts/text_extract.py input.hwpx --format json --include-nested --out output.json
```

플레이스홀더 전역 치환:

```bash
python scripts/zip_replace_all.py template.hwpx output.hwpx --replace "{학교명}=테스트초" "{담당자}=홍길동" --auto-fix-ns
python3 scripts/zip_replace_all.py template.hwpx output.hwpx --replace "{학교명}=테스트초" "{담당자}=홍길동" --auto-fix-ns
```

namespace 정리만 수행:

```bash
python scripts/fix_namespaces.py output.hwpx --inplace --backup
python3 scripts/fix_namespaces.py output.hwpx --inplace --backup
```

## 예제
Expand All @@ -208,6 +278,16 @@ python scripts/fix_namespaces.py output.hwpx --inplace --backup
- `examples/02_extract_and_inspect.py`: 텍스트 추출, 문단 순회, 표 개수 확인
- `examples/03_template_replace.py`: 템플릿 치환, namespace 정리, 결과 저장

## 설치 후 문제를 만나면

먼저 아래를 확인한다.
- `python3 -m pip install -U python-hwpx lxml`를 다시 실행했는가
- `python3 scripts/quickcheck.py`가 통과하는가
- 결과 파일이 `examples/out/` 아래 생성되는가
- 입력 파일이 실제 `.hwpx` ZIP 패키지인가

문제가 재현되면 `quickcheck.py` 출력과 함께 이슈를 남기면 된다.

## 운영 메모

- `save()` 대신 `save_to_path()`를 사용한다.
Expand All @@ -220,5 +300,28 @@ python scripts/fix_namespaces.py output.hwpx --inplace --backup
- GitHub: <https://github.com/airmang>
- Base Library: <https://github.com/airmang/python-hwpx>

## 제안서/기획안 생성

`python-hwpx`의 proposal preset이 설치된 환경에서는 자연어 요청을 `ProposalSpec`으로 정리한 뒤 HWPX 제안서를 바로 생성할 수 있다.

```bash
python3 examples/04_create_proposal.py
python3 scripts/quickcheck.py --proposal
```

권장 흐름:

1. 사용자 요청을 제목, 부제, 기관, 작성자, 핵심 요약, 섹션, 예산 항목, 기대 효과, 마무리 문구로 나눈다.
2. `create_proposal_document(proposal_spec)`로 HWPX를 생성한다.
3. `inspect_proposal_quality(output.hwpx)`로 필수 섹션, 표, validation, rubric 평균, `sample_match` 결과를 확인한다.
4. 평균 4.0 미만, `sample_match.pass == false`, 특정 dimension 실패, 필수 섹션 누락이면 `ProposalSpec`을 보강해 재생성한다.

샘플 기반 주의사항:

- 좋은 예시는 한국식 제안서/계획서의 메타데이터 표, 명확한 제목, 단계적 본문 흐름을 갖는다.
- 나쁜 예시는 과도한 BMP 이미지 payload와 agent가 재현하기 어려운 이미지 중심 구조를 포함한다.
- v2 리포트의 `visual_review_required=True`는 렌더러/픽셀 diff 없이 package/XML/text proxy만 확인했다는 뜻이다.
- 예제/문서에는 이름, 전화번호, 이메일, 주소 등 PII를 redaction 없이 넣지 않는다.

## License
Apache License 2.0. See LICENSE and NOTICE.
14 changes: 14 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,17 @@ python3 examples/02_extract_and_inspect.py examples/out/03_replaced.hwpx
- 결과 파일을 덮어쓸 때는 `--backup`을 사용한다.
- 자동화 결과물은 가능한 한 한 번 다시 열어본다.
- API 세부 옵션이나 최신 시그니처가 필요하면 항상 [`references/api.md`](references/api.md)를 먼저 읽는다.

## 제안서/기획안 생성 workflow

사용자가 “제안서”, “기획안”, “계획서” 형태의 새 HWPX 생성을 요청하면 저수준 XML 조작보다 `python-hwpx`의 proposal preset을 먼저 사용한다.

1. 자연어 요청을 `ProposalSpec` JSON으로 정규화한다.
2. `from hwpx.presets import create_proposal_document, inspect_proposal_quality`를 사용한다.
3. 생성 직후 `inspect_proposal_quality()`로 구조, 표, payload, validation, rubric 점수, `sample_match`를 확인한다.
4. 평균 점수 4.0 미만, `sample_match.pass == false`, 특정 sample-match dimension 실패, 필수 섹션 누락이면 `ProposalSpec`을 보강해 다시 생성한다.
5. 샘플에서 배운 anti-pattern: 큰 BMP 이미지에 의존하는 문서, 표/메타데이터가 이미지처럼 박힌 문서, 연락처/이메일/주소 등 PII가 redaction 없이 예제에 노출되는 문서는 피한다.
6. `visual_review_required=True`는 렌더러/픽셀 diff 없이 sample-derived proxy metric만 통과했다는 제한으로 해석한다.

예제: `examples/04_create_proposal.py`
검증: `python3 scripts/quickcheck.py --proposal`
54 changes: 54 additions & 0 deletions examples/04_create_proposal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""제안서/기획안 HWPX를 agent-first preset으로 생성하는 예제."""

from __future__ import annotations

from pathlib import Path

from hwpx.presets import create_proposal_document, inspect_proposal_quality


def main() -> None:
output_path = Path(__file__).resolve().parent / "out" / "04_proposal.hwpx"
output_path.parent.mkdir(parents=True, exist_ok=True)

proposal_spec = {
"title": "AI 융합형 교육실 구축 제안서",
"subtitle": "학생 맞춤형 디지털 학습 공간 구축 및 운영 계획",
"organization": "샘플 고등학교",
"author": "교육혁신팀",
"date": "2026-05-06",
"metadata": {"문서유형": "제안서", "검토단계": "초안"},
"executive_summary": "AI 융합형 교육실을 구축하여 수업, 평가, 기록을 연결하는 학습 환경을 조성합니다.",
"sections": [
{"title": "추진 배경 및 문제 정의", "paragraphs": ["디지털 기반 수업은 확대되고 있으나 실습 공간과 운영 체계가 분리되어 있습니다."]},
{"title": "제안 내용", "bullets": ["AI 실습 존과 협업 학습 존 구성", "교원 연수와 수업 적용 컨설팅 운영"]},
{"title": "구축 및 운영 계획", "paragraphs": ["준비, 구축, 시범 운영, 확산의 네 단계로 추진합니다."]},
],
"budget_items": [
{"item": "기자재", "amount": "5,000,000원", "note": "노트북 및 주변기기"},
{"item": "연수 운영", "amount": "1,000,000원", "note": "교원 워크숍"},
],
"expected_outcomes": ["수업 참여도 향상", "학생별 피드백 강화", "AI 활용 프로젝트 성과 축적"],
"closing": "본 제안서를 검토 후 승인 요청드립니다.",
}

doc = create_proposal_document(proposal_spec)
try:
doc.save_to_path(output_path)
finally:
doc.close()

report = inspect_proposal_quality(output_path)
print(f"[OK] wrote: {output_path}")
print(f"[OK] rubric average: {report['rubric_average']} pass={report['pass']}")
print(
"[OK] sample match: "
f"average={report['sample_match']['average']} "
f"pass={report['sample_match']['pass']} "
f"visual_review_required={report['visual_review_required']}"
)


if __name__ == "__main__":
main()
42 changes: 42 additions & 0 deletions references/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,45 @@ from hwpx.opc.package import HwpxPackageError, HwpxStructureError
- 손상된 ZIP/OWPML 구조를 다룰 때는 `HwpxPackageError`, `HwpxStructureError`를 잡는다.
- `.hwp`는 대상이 아니다. `.hwpx`만 지원한다.
- ZIP-level 문자열 치환 뒤에는 `scripts/fix_namespaces.py` 또는 `scripts/zip_replace_all.py --auto-fix-ns`로 후처리한다.

## Proposal preset

`python-hwpx`의 proposal preset은 agent-first 제안서/기획안 생성을 위한 고수준 API다.

```python
from hwpx.presets import create_proposal_document, inspect_proposal_quality

proposal_spec = {
"title": "AI 융합형 교육실 구축 제안서",
"executive_summary": "핵심 요약을 작성합니다.",
"sections": [
{"title": "추진 배경 및 문제 정의", "paragraphs": ["배경 설명"]},
{"title": "제안 내용", "bullets": ["핵심 제안 1"]},
{"title": "구축 및 운영 계획", "paragraphs": ["추진 일정"]},
],
"budget_items": [{"item": "기자재", "amount": "5,000,000원", "note": "노트북"}],
"expected_outcomes": ["수업 참여도 향상"],
"closing": "검토 후 승인 요청드립니다.",
}

doc = create_proposal_document(proposal_spec)
doc.save_to_path("proposal.hwpx")
doc.close()

report = inspect_proposal_quality("proposal.hwpx")
assert report["rubric_average"] >= 4.0
assert report["sample_match"]["pass"] is True
```

주요 함수:

- `create_proposal_document(spec, *, preset="clean_korean_proposal") -> HwpxDocument`
- `inspect_proposal_quality(source) -> dict`

품질 기준:

- rubric 평균 4.0 이상
- `sample_match.average` 4.0 이상 및 실패 dimension 없음
- critical validation error 없음
- 생성 파일 payload 5MB 미만 권장
- `visual_review_required=True`이면 렌더러/픽셀 diff 없이 proxy 기준만 통과한 상태
Loading