Metadata-Version: 2.4
Name: reportgen-pipeline
Version: 0.3.1
Summary: LLM 기반 국책과제계획서·제안서·보고서(HWP/HWPX/DOCX/PDF/Markdown/HTML) CLI 자동 작성 파이프라인
License: MIT License
        
        Copyright (c) 2026 Yoojin Nam
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/kaismin82/ReportGen-pipeline
Project-URL: Repository, https://github.com/kaismin82/ReportGen-pipeline
Project-URL: Bug Tracker, https://github.com/kaismin82/ReportGen-pipeline/issues
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: python-hwpx>=0.1.0
Requires-Dist: lxml>=4.9.0
Requires-Dist: pypdf>=3.0.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: python-docx>=1.0.0
Provides-Extra: test
Requires-Dist: pytest>=7.0.0; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Dynamic: license-file

<h1 align="center">ReportGen</h1>

<p align="center">
  <i>A CLI pipeline for AI-assisted drafting of Korean national R&D project plans, proposals, and technical reports (HWP/HWPX) — from form analysis to a finished document, in partnership with an external LLM agent.</i>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/python-3.10%2B-blue" alt="Python">
  <img src="https://img.shields.io/pypi/v/reportgen-pipeline" alt="PyPI">
  <img src="https://img.shields.io/badge/license-MIT-green" alt="License">
  <img src="https://img.shields.io/badge/platform-linux%20%7C%20macOS%20%7C%20windows-lightgrey" alt="Platform">
  <img src="https://img.shields.io/badge/tests-103%2B_passing-success" alt="Tests">
  <img src="https://img.shields.io/badge/table_types-5-blueviolet" alt="5 Table Types">
</p>

---

## Overview

ReportGen is a **command-line tool**, not a hosted service or a model. You give it a form — a national R&D project plan, a proposal, a technical report, an IRB protocol, any similarly structured institutional document — plus reference material, and it does two things:

1. **Analyzes** the form's structure and emits a mapping-request artifact that any external LLM agent can read.
2. **Applies** the agent-authored mapping back onto the form to produce a finished document.

The pipeline itself never calls an LLM API. Content generation is delegated entirely to whichever external agent drives the CLI — Claude Code, OpenCode, Codex, or any other coding agent/harness. This means no API-key management and no lock-in to a single model provider inside the tool, and it's why the same pipeline works unmodified whether you're filling out a dementia-research R&D plan, a public-works proposal, or a mid-term technical report.

**HWP/HWPX is ReportGen's native engine** — `reportgen` parses and edits HWPX XML directly (`StructureAnalyzer`/`SmartFillEngine`, no external converter in the loop). For **`.docx`, `.pdf`, `.md`, and `.html`** templates, the same analyze → design → apply → verify methodology applies, routed through the right specialized tool for that format instead of a from-scratch HWPX-only parser — see [Supported Template Formats](#supported-template-formats). Reference material (the *source facts* you feed the design step) is supported natively across all of these formats regardless of what the template itself is in. ReportGen ships through three channels — PyPI, standalone binaries, and a Claude Code plugin — so it installs cleanly in any environment.

---

## Features

- 🔍 **Automatic HWPX structure analysis** — TOC detection, 5-way table classification (DATA/INSTR/SKIP/SCHED/IDENT), and paragraph placeholder detection in one pass.
- 🤝 **Mapping-request artifact generation** — emits `request.json` + `prompt.md` for any external LLM agent to read. Zero internal LLM calls.
- ✍️ **FieldMapping apply engine** — takes the agent-authored JSON mapping and writes it into the HWPX XML via three precise actions: `replace` / `fill_cell` / `insert_after`.
- 🎯 **Adaptive fill — no per-form hardcoding** — table cells can be targeted by structural *intent* instead of raw coordinates: a `label` field resolves to whatever cell sits next to that label text, and a `schedule` field expands a Gantt-chart task into the right monthly cells — both computed fresh from each document's own structure, so the same code works on any form of that shape.
- 🔤 **Font consistency & line-wrap safety** — automatically applies a 12pt non-bold Malgun Gothic `charPr`, and strips stale `<hp:linesegarray>` caches so Hangul Word doesn't render overlapping text after reflow.
- 📎 **Reference-material ingestion, format-agnostic** — TXT/MD/PDF/HWPX/DOCX/HTML reference files are all parsed automatically and folded into the mapping-request payload, regardless of what format the *template* itself is in.
- 🧭 **Multi-format template routing** — `.hwp`/`.hwpx` templates are filled by ReportGen's native engine; `.docx`/`.pdf`/`.md`/`.html` templates are filled by routing the same 4-step methodology through the matching specialized tool (see [Supported Template Formats](#supported-template-formats)).
- 🛡️ **Structural verification gate** — checks for table loss, coordinate anomalies, and paragraph fill rate right after the pipeline runs, and measures accurate fill rate excluding SKIP/SCHED tables.
- 🔁 **HWP → HWPX conversion** — converts binary HWP to ZIP-based HWPX via a Java fat JAR.
- 📦 **Three distribution channels** — PyPI (update-notification), standalone binary (true self-update), and the Claude Code plugin marketplace. See [Installation](#installation) for details on each.

---

## Quick Start

```bash
pip install reportgen-pipeline

# Phase 1 — analyze the form & emit a mapping request (no LLM call)
reportgen \
  --template  input/project-plan-form.hwpx \
  --reference input/project-summary.txt input/rfp.pdf \
  --emit-mapping-request request.json

# (an external LLM agent reads request.json and writes mappings.json)

# Phase 2 — apply the mapping & produce the finished HWPX
reportgen \
  --template input/project-plan-form.hwpx \
  --mappings mappings.json \
  --output   output/result.hwpx \
  --measure
```

---

## How It Works

1. **Form analysis (Phase 1)** — `StructureAnalyzer` parses the HWPX form's table/paragraph structure, auto-classifies every table into one of 5 types (DATA/INSTR/SKIP/SCHED/IDENT), detects fill targets, and emits `request.json` (full detail) plus a compact digest from `digest.py` (a few dozen KB, via `--emit-digest`) and an optional `prompt.md`. **This phase never calls an LLM API.**
2. **An external LLM agent fills it in** — Claude Code, OpenCode, or any other agent designs the fill plan from the compact digest (not the full `request.json`, especially for large forms), targets only the specific elements it needs from `request.json`, and writes `mappings.json` following the `FieldMapping[]` contract. Swapping models or providers never requires touching the pipeline code.
3. **Mapping apply (Phase 2)** — `SmartFillEngine` writes `mappings.json` into the HWPX XML: `replace` (paragraphs, with stale linesegarray cleared), `fill_cell` (table cells, coordinates verified against the real document — or resolved dynamically from a `label`/`schedule` intent), `insert_after` (new paragraphs, 12pt Malgun Gothic applied).
4. **Verify + measure** — `verify_output` gates on table loss and paragraph fill rate, and `measure_fill_rate` computes an accurate fill rate against the original template, excluding SKIP/SCHED tables. The finished `output.hwpx` opens directly in Hangul Word Processor.

### Core design principles

| Principle | What it means |
|---|---|
| **Form-invariant** | Instruction tables ("delete before submission"), diagram-like tables, and identity tables are preserved as-is; only genuine data tables get filled |
| **Line-wrap safety** | Stale `<hp:linesegarray>` caches are stripped so Hangul Word reflows cleanly, with no overlapping text |
| **Dynamic classification, never hardcoded** | Gantt charts, org charts, and empty grids are identified by structural pattern, not by memorizing any one form's layout |
| **Content stays external** | Content generation is delegated to an external agent — model and provider stay fully swappable |
| **Accurate measurement** | Fill rate is computed against the original template, excluding SKIP/SCHED tables |
| **Digest-first analysis** | The design phase reads a compact digest instead of the full `request.json` — measured example: a 29-table form went from a 1.5MB `request.json` to a 45KB digest |

---

## Supported Template Formats

The file format of the **template you're filling out** determines which engine actually does the work. Every format below follows the same four-step methodology — ① analyze structure, ② have an external agent design the fill plan, ③ apply it, ④ verify — but only HWP/HWPX runs through ReportGen's own purpose-built parser. The rest are routed to the right specialized tool so the same methodology applies without ReportGen pretending to have a native engine it doesn't:

| Template format | ① Analyze | ③ Apply | ④ Verify |
|---|---|---|---|
| `.hwp` | Convert to `.hwpx` first (`hwp2hwpx`), then use the row below | (same) | (same) |
| `.hwpx` | `reportgen --emit-mapping-request --emit-digest` — native `StructureAnalyzer` | `reportgen --mappings` — native `SmartFillEngine` | `reportgen --measure` — deterministic script |
| `.docx` | The `docx` skill: an existing file is unzipped and read with `python-docx` (or `pandoc -t markdown` for a quick read); a new file is built with `docx-js` | Same skill: cells/paragraphs are edited directly with `python-docx`, then rezipped (or generated fresh with `docx-js`) | Render to PDF (LibreOffice `soffice --headless --convert-to pdf`, or Word via COM automation) and inspect the page images directly |
| `.pdf` | The `pdf` skill: check whether the PDF has real form fields | Fill the form fields if present, otherwise overlay text at the right coordinates | Render pages to images and inspect directly |
| `.md` / `.html` | Read directly — both are plain text, so no separate structural parser is needed | Edit/Write directly | Read back and check |

**Reference material is unaffected by this table** — `--reference` accepts `.txt`/`.md`/`.pdf`/`.hwpx`/`.docx`/`.html`(`.htm`) in any combination no matter what format the template is in, because `reference_reader.py` parses each one directly (via `pypdf`, `python-hwpx`'s `TextExtractor`, `python-docx`, or a tag-stripping HTML reader). Legacy binary `.hwp`/`.doc` reference files need a one-time conversion first (`hwp2hwpx`/`soffice --convert-to docx`) — the error message tells you the exact command.

---

## LLM Allocation — how model tiers are assigned

**ReportGen's own code contains no LLM calls anywhere.** It is a deterministic CLI: Phase 1 and Phase 2 are pure structural analysis and XML editing, and `--measure` is a pure scoring script. Every piece of authored content comes from whatever external agent invokes the CLI — which means *you* (or your coding agent) decide which model tier does which job. The tool is designed around a specific 3-stage division of labor so that model spend tracks task difficulty instead of being uniform:

| Stage | Tier required | Input it should read | Why |
|---|---|---|---|
| **① Form analysis & fill design** | **High-tier** (e.g. Opus-class, GPT-5.6-class "high"/"solve" reasoning, or an equivalent top-tier reasoning mode) | The compact **digest** (`--emit-digest`), *not* the full `request.json` | This is the step that decides *what* goes in each field, *how much* content each section needs, and *where* a large table should be preserved vs. filled — the single highest-leverage decision in the whole run, so it deserves the strongest available model. Reading the full `request.json` here (which can be hundreds of KB to >1MB on large forms) burns tokens without improving the decision, since the digest already carries everything structural: element indices, previews, table classifications, and fill coordinates. |
| **② Field content generation** | **Mid-tier** (e.g. Sonnet-class, GPT-5.6 standard, or a comparable general-purpose coding-agent model) | The Phase ① design plan, plus **targeted** lookups into `request.json` by `element_index` only when exact source text or merge structure is needed | Once the design is set, writing the actual sentence/number for each target is comparatively mechanical — a mid-tier model handles it reliably and far more cheaply. |
| **③ Verification & fill-rate measurement** | **None — deterministic code** | `--measure` output | This is fully computed by `measure_fill_rate.py`; no model judgment is involved. Only re-invoke a mid-tier model here if the measured fill rate looks abnormally low or the output content looks suspicious — a high-tier model is never needed for this step. |

A few corollaries that follow directly from this design:

- **Never use a lightweight/mini-tier model at any stage.** The whole pipeline is calibrated around "one strong analysis pass, one competent fill pass" — a cheap model at either stage tends to produce sparse content or mis-scoped table decisions, which is exactly the failure mode the digest/design split is meant to prevent.
- **Simple forms don't need the two-stage split.** A form with a handful of tables and under ~20 paragraph targets is fine end-to-end in a single mid-tier session — reserve the high-tier design pass for large forms (10+ tables, multi-year tables, complex row/merge structures), where it earns its cost.
- **Cache your research.** If you're filling out many proposals in the same domain, save web-research findings (e.g. `input/research_facts.md`) and reuse them — re-searching the same domain facts on every run is wasted spend, independent of which model tier you're using.
- **Prefer intent over coordinates when authoring `mappings.json`.** A `FieldMapping` entry can carry a `label` field (resolved to whatever cell sits next to that label text) or a `schedule` field (`{task_row, start_month, end_month, mark}`, expanded into the right Gantt-chart cells) instead of hand-counted `table_row`/`table_col` — this is what makes the same mapping-authoring approach transfer across different forms without new per-form logic. See the skill's `SKILL.md` (`.claude/skills/reportgen/SKILL.md`) for the full `FieldMapping` schema and worked examples.

---

## CLI

```
--template   <HWPX form path>              (required)
--output     <output HWPX path>            (required for Phase 2)
--reference  <file1> <file2> ...           TXT/MD/PDF/HWPX/DOCX/HTML reference files
--data       <user_data.json>              additional structured data (optional)
--instructions "extra instructions"        additional instructions for the agent (optional)
--emit-mapping-request <request.json>      Phase 1: emit the mapping request
--emit-digest <digest.json>                Phase 1: emit the compact structural digest (recommended primary input for the design stage)
--prompt-output <prompt.md>                Phase 1: emit a Markdown prompt (optional)
--mappings   <mappings.json>               Phase 2: apply the mapping
--analysis-output <analysis.json>          save the structure analysis result (optional)
--measure                                  print the fill rate on completion
--version                                  show version
--upgrade                                  standalone-binary channel: self-upgrade immediately
--no-update-check                          skip the startup update check
--verbose
```

### After converting HWP → HWPX

```bash
java -jar java/hwp2hwpx-fat.jar input/form.hwp input/form.hwpx
reportgen --template input/form.hwpx --emit-mapping-request request.json
```

### Measuring fill rate separately

```bash
python scripts/measure_fill_rate.py --file output/result.hwpx --template input/form.hwpx
```
```
=======================================================
Fill Rate Report: result.hwpx  [template]
=======================================================
TABLE CELLS (DATA tables only, SKIP/SCHED excluded):
  Total data cells  : 86
  Filled cells      : 86
  Fill rate         : 100.0%  [##################################################]

PARAGRAPH TARGETS (from template):
  Total targets     : 66
  Filled            : 60
  Fill rate         : 90.9%  [#############################################-----]
=======================================================
```

---

## Installation

| Channel | Command | Notes |
|---|---|---|
| **PyPI** (recommended) | `pip install reportgen-pipeline` | Public distribution; checks for a new version once a day at runtime and prints a `pip install -U` notice |
| **Standalone binary** | Download `reportgen-<os>-<arch>` from [Releases](https://github.com/kaismin82/ReportGen-pipeline/releases) | For internal/collaborator use (private repo, requires auth); **true self-update** in the OpenCode style |
| **Source** (for development) | `git clone` then `pip install -e .` | Includes the `reportgen` CLI, editable install |

### Prerequisites

- **Python 3.10+**
- **Java 11+ / Maven** (only needed for HWP → HWPX conversion)
- Filling a `.docx` or `.pdf` template pulls in the Claude Code `docx`/`pdf` skills for the apply/verify steps (see [Supported Template Formats](#supported-template-formats)); no extra setup is needed to use `--reference` with those formats, since `python-docx` is a core dependency

### PyPI channel details

```bash
pip install reportgen-pipeline
pip install -U reportgen-pipeline   # upgrade
reportgen --version
```

pip deliberately never reinstalls itself automatically (this is correct, safe behavior). The CLI only prints a **"a new version is available — run `pip install -U`"** notice, the same way `gh` or `npm` do (disable with `REPORTGEN_NO_UPDATE_CHECK=1`). This repository is private, but PyPI is a separate public registry, so this channel is reachable by **anyone**.

### Standalone binary channel details

```bash
gh release download v0.3.0 --repo kaismin82/ReportGen-pipeline \
  --pattern "reportgen-<os>-<arch>*"
./reportgen-windows-x86_64.exe --template ... --emit-mapping-request request.json
```

> ⚠️ Because this repository is **private**, this channel only works for people with repo access (anonymous requests get a 404 from the GitHub API). Authentication comes from `gh auth token` or the `GITHUB_TOKEN`/`GH_TOKEN` environment variable. Use the PyPI channel if you need public distribution.

Unlike the pip package, a standalone binary owns nothing but itself (no shared venv or pinned-version environment to break), so on every run, if it detects a newer version it **replaces itself and immediately re-executes the same command** — the same mechanism OpenCode uses for `opencode upgrade`. Force it immediately with `reportgen --upgrade`, or disable it with `REPORTGEN_NO_AUTOUPDATE=1`.

### Source install & conda caveat

```bash
git clone https://github.com/kaismin82/ReportGen-pipeline.git
cd ReportGen-pipeline
pip install -r requirements.txt      # dependencies only
pip install -e .                     # or an editable install (includes the reportgen CLI)
bash setup.sh --java                 # (optional) build the HWP→HWPX converter
```

conda environments may already have a **different** package also named `hwpx` installed (`pip install hwpx` ❌ ≠ `pip install python-hwpx` ✅):
```bash
pip install python-hwpx --force-reinstall
```

---

## Architecture

| Module | Role |
|---|---|
| `universal_pipeline.py` | 📌 Main CLI entry point (the `reportgen` command) |
| `document_model.py` | Data classes (DocumentElement, TableInfo, FieldMapping) |
| `structure_analyzer.py` | HWPX structure analysis · table/paragraph classification |
| `dynamic_template_analyzer.py` | Analysis orchestrator (no LLM calls) |
| `smart_fill_engine.py` | HWPX XML edit engine (replace/fill_cell/insert_after) |
| `mapping_resolve.py` | Resolves `label`/`schedule` intent mappings into concrete `fill_cell` mappings |
| `schedule_resolve.py` | Expands a Gantt-chart task intent into the right monthly cells, from the table's own structure |
| `label_resolve.py` | Resolves a `label` intent to its value cell by text match + spatial rule |
| `identity_fill.py` | Auto-fills IDENT (identity/institution) tables from `user_data`, with zero fabrication |
| `table_resolve.py` | Nested-table resolver (passes through layout frames) |
| `toc_detection.py` | TOC detection · table-classification helpers · blank-detection regex |
| `reference_reader.py` | Reference-file parser: TXT/MD/PDF/HWPX/DOCX/HTML |
| `schedule_handler.py` | Gantt-chart metadata extraction |
| `verify_output.py` | Output structural verification gate |
| `measure_fill_rate.py` | Fill-rate measurement (accurate, computed against the template) |
| `digest.py` | Compact structural digest generation (computed from `elements` alone; primary input for the design stage) |
| `hwpx_compat.py` / `hwpx_utils.py` | HWPX-package compatibility layer · shared utilities |
| `update_check.py` | PyPI latest-version check/notice (pip channel) |
| `self_update.py` | Standalone-binary self-update (private repo, requires auth) |
| `bump_version.py` | Bulk version bump · release · CI consistency check |

```
ReportGen-pipeline/
├── scripts/            # all modules listed above + archive/ (legacy scripts)
├── tests/               # pytest suite (103+ passing)
├── java/                # HWP→HWPX converter (Convert.java, hwp2hwpx-fat.jar)
├── .claude-plugin/      # Claude Code marketplace catalog
├── reportgen/           # Claude Code plugin (distribution copy: plugin.json + SKILL.md)
├── .claude/skills/      # project-scoped skill (plugin mirror)
├── .github/workflows/   # ci.yml · publish.yml (PyPI) · build-binaries.yml
├── docs/                # design proposals
├── pyproject.toml       # package metadata · build config
└── requirements.txt
```

### Table classification scheme

| Class | Criteria | Handling |
|---|---|---|
| `DATA` | Ordinary data table | Filled by the agent (`fill_cell`) |
| `INSTR` | Contains authoring instructions ("delete before submission") | **Preserved** (never touched) |
| `SKIP` | Org chart, WBS, empty grid | Preserved as-is |
| `SCHED` | Gantt chart (task rows + monthly columns) | Filled via a `schedule` intent mapping |
| `IDENT` | PI/institution identity table | Auto-filled from `user_data` if present, otherwise preserved |

---

## Claude Code Skill

ReportGen ships as a **CLI-driven Claude Code skill** — invoking `/reportgen` doesn't run a hosted service; it drives the same `reportgen` CLI described above from your local shell, with an external agent (Claude Code itself, in this case) doing the analysis and fill-content authoring per the [LLM Allocation](#llm-allocation--how-model-tiers-are-assigned) model. You can use it two ways:

**Option 1 — Plugin marketplace (recommended, auto-upgrading)**
```
/plugin marketplace add kaismin82/ReportGen-pipeline
/plugin install reportgen@reportgen-marketplace
```
New releases are detected and downloaded automatically in the background, and activate automatically **the next session (restart)** with no command needed (run `/reload-plugins` once to pick it up immediately in the current session). ⚠️ Since this repository is private, this channel is also collaborator-only.

**Option 2 — Directly from the repository**
Cloning the repository makes `.claude/skills/reportgen/SKILL.md` recognized immediately as a project-scoped skill. Update with `git pull`.

```
Use the /reportgen skill to draft a project plan matching this form.
- template: input/project-plan-form.hwpx
- reference files: input/project-summary.txt input/rfp.pdf
- output file: output/result.hwpx
```

The skill runs Phase 1 automatically → drafts `mappings.json` from reference material and web search → runs Phase 2 → confirms fill rate with `--measure`.

---

## Why Not Just Ask Codex / Claude Code / OpenCode Directly?

You can always just type "please fill out this template" at any coding agent, without this skill. Here's concretely what that gives up, and what invoking `/reportgen` (or driving the CLI directly) changes instead:

| | Asking the agent directly | Using `/reportgen` |
|---|---|---|
| Structure discovery | The agent reads the file once, informally, and guesses which paragraphs/cells are blanks vs. instructions to leave alone | Every table is explicitly classified (DATA/INSTR/SKIP/SCHED/IDENT) and every paragraph placeholder is detected *before* any content is written — instruction tables and diagram-like tables are never touched by accident |
| Table cell targeting | The agent typically hand-counts rows/columns from what it just read, which breaks silently the next time the form's layout differs slightly | `label`/`schedule` intent fields resolve coordinates dynamically from the document's own structure — the same mapping-authoring approach transfers to a differently-shaped form of the same kind, with no new code |
| Token cost on large forms | The agent re-reads the entire document — and often re-reads it again mid-edit — so cost scales with full document size every time | The design step reads a compact digest (tens of KB) instead of the full structure dump — measured example: a 29-table form went from a 1.5MB dump to a 45KB digest |
| Model tier | One model does everything at whatever tier you happened to invoke — overpaying for mechanical fill-ins, or underpaying for the one decision (what goes where, how much) that actually determines quality | Explicit 3-stage tiering: high-tier for the structural design decision, mid-tier for writing the actual content, no model at all for verification — see [LLM Allocation](#llm-allocation--how-model-tiers-are-assigned) |
| Verification | "Looks right" by skimming the output, if it's checked at all | A deterministic fill-rate gate reports exactly which targets are still empty or under length, measured against the *original* template — not a self-assessment by the same model that wrote the content |
| Identity/PI fields | An agent under a blanket "fill in everything" instruction will sometimes invent a plausible-looking name, phone number, or ID rather than leave a field blank | Zero-fabrication by design: unmatched or ambiguous identity fields are left blank with a bracketed "needs input" marker instead of guessed |
| Repeatability across forms | Every new template is a fresh, unstructured task — nothing from the last run transfers | The same analyze → design → apply → verify contract applies to every form of a given file type; only the content changes |

None of this makes ReportGen "smarter" than the agent — the agent still writes every sentence. What it adds is the scaffolding around that: a structural contract the agent fills in a repeatable way, and a deterministic check afterward that doesn't depend on the same agent grading its own homework.

### Prompting examples

**Without the skill** (works, but gives up everything in the table above):
```
Please fill out input/irb-protocol-template.docx as a research plan for
an AI model that classifies 5 subtypes of intracranial hemorrhage on
non-contrast CT, and save it as output/protocol.docx.
```

**With the skill** — same request, routed through the methodology (template is `.docx`, so this routes to the `docx` skill per [Supported Template Formats](#supported-template-formats)):
```
/reportgen

Use the reportgen skill to fill out the attached template.
- template: input/irb-protocol-template.docx
- reference: none — use web search for the clinical/technical background
- topic: AI model for 5-subtype classification of intracranial hemorrhage on
  non-contrast CT, with automated structured report generation
- output: output/protocol.docx
```

For an HWPX national R&D plan with reference material already on hand (runs through ReportGen's native engine):
```
/reportgen

Use the reportgen skill to draft a project plan matching this form.
- template: input/project-plan-form.hwpx
- reference files: input/project-summary.txt input/rfp.pdf
- output file: output/result.hwpx
```

You don't need to spell out the phase mechanics (digest-first analysis, model tiering, verification) in the prompt itself — that's what `SKILL.md` already encodes. What you supply is the template, whatever reference material exists (or an explicit note that there is none and web search should fill the gap), and where the output should go.

---

## Versioning & Release Automation

Version strings (`pyproject.toml`, `universal_pipeline.py`, `plugin.json`) are managed as a single source of truth by `scripts/bump_version.py`:

```bash
python scripts/bump_version.py 0.3.0               # bump the version everywhere (incl. SKILL.md mirror sync)
python scripts/bump_version.py 0.3.0 --release      # + commit · tag (v & reportgen--v) · push · GitHub release
python scripts/bump_version.py --check              # CI consistency check (run automatically by ci.yml)
```

Once a GitHub Release is published, two workflows follow automatically:
- `publish.yml` → publishes sdist/wheel to PyPI via Trusted Publishing (OIDC) — no token needed (requires a one-time PyPI pending-publisher + GitHub `pypi` environment setup)
- `build-binaries.yml` → builds, verifies, and attaches standalone binaries for the Linux/macOS/Windows matrix as release assets

---

## FAQ

**Q: I get `ImportError: cannot import name 'HwpxPackage' from 'hwpx'`.**
A conflicting, unrelated package also named `hwpx` is installed in your conda environment. Run `pip install python-hwpx --force-reinstall`.

**Q: After Phase 1, which files do I hand to the external agent?**
Hand it `request.json` (from `--emit-mapping-request`), the compact digest (from `--emit-digest`), and `prompt.md` (from `--prompt-output`) if you generated one. The agent should design the fill plan from the digest, target only the specific elements it needs from `request.json`, and write a `FieldMapping[]` JSON array to `mappings.json`.

**Q: The analysis stage burns too many tokens on a large, table-heavy form.**
Generate the compact digest with `--emit-digest` and feed that to the design-stage model instead of `request.json`. The digest doesn't repeat each element's full text — it carries only indices, previews, table classifications, and fill coordinates, compressing a 29-table form's `request.json` from 1.5MB down to 45KB in one measured case. Only look up `request.json` by `element_index` when you need a specific element's exact source text.

**Q: My paragraph fill rate comes out low.**
Always pass `--template` alongside `--measure`. Fill quality itself depends on the external agent's model and prompt quality — see [LLM Allocation](#llm-allocation--how-model-tiers-are-assigned).

**Q: An instruction table got filled in.**
`structure_analyzer.py`'s `_is_instruction_table()` treats a "delete before submission" marker as a strong signal. If your form doesn't carry that marker, add another instruction signal to `_DELETE_RE`.

**Q: The Gantt chart / schedule table gets filled oddly.**
Prefer a `schedule` intent mapping over hand-picked coordinates — it lets `schedule_resolve.py` detect the month-header row and task blocks from the table's own structure. Structural metadata is also exposed via `schedule_handler.detect_schedule_meta()` in `request.json` if you need to inspect it directly.

**Q: My template is a `.docx` or `.pdf`, not `.hwpx` — can I still use this?**
Yes. `reportgen` itself only parses HWPX, but the skill routes `.docx` templates to the `docx` skill and `.pdf` templates to the `pdf` skill, applying the same analyze → design → apply → verify methodology through each tool's native editing approach. See [Supported Template Formats](#supported-template-formats). Reference material, separately, works natively in `--reference` for all of TXT/MD/PDF/HWPX/DOCX/HTML regardless of the template's own format.

**Q: Why do the pip channel and the standalone-binary channel self-update differently?**
A pip package lives inside a shared venv / pinned-version environment, so silently bumping its own version could break a user's lockfile — hence "notify only." A standalone binary owns nothing but itself, so a true self-replace is safe.

---

## Development

```bash
pip install -e ".[test]"
pytest tests/ -v                                                       # full suite
pytest tests/test_output_qa.py tests/test_smart_fill_engine_p0.py -v   # core invariants only
python scripts/bump_version.py --check                                 # version consistency check
```

| Test file | Verifies |
|---|---|
| `test_output_qa.py` | Absence of stale linesegarray · cell placement · resolver invariants |
| `test_smart_fill_engine_p0.py` | replace/fill_cell/linesegarray stripping |
| `test_structure_analyzer_p1.py` | TOC detection · header extraction · instruction tables |
| `test_mapping_artifact_handoff.py` | Phase 1/2 CLI round-trip |
| `test_self_update.py` | Platform → asset-name mapping, version comparison, asset-selection logic |
| `test_digest.py` | Table-classification precedence, fill-target extraction, large-table coordinate-omission threshold |
| `test_e2e_pipeline.py` | Full flow from structure analysis through SmartFillEngine |
| `test_korean_edge_cases.py` | Full-width brackets, ZWSP, full-width-space blank detection |
| `test_schedule_table.py` | Gantt-table detection · metadata extraction |
| `test_mapping_resolve.py` | `label`/`schedule` intent dispatch and fail-soft skipping |
| `test_schedule_resolve.py` | Dynamic month/task-block detection across varied Gantt shapes |
| `test_label_resolve.py` | Label text matching + spatial value-cell resolution |
| `test_identity_fill.py` | Zero-fabrication identity auto-fill, duplicate-label safety |
| `test_quality_gates.py` | Section-tier classification and content-length quality gates |
| `test_reference_p2.py` | Reference-file reading across TXT/PDF/HWPX/DOCX/HTML, format dispatch, clear errors on legacy `.doc`/`.hwp` |

---

## License & Credits

MIT — see [LICENSE](LICENSE). Free to use, modify, and distribute.

| Project | Role | License |
|---|---|---|
| [python-hwpx](https://pypi.org/project/python-hwpx/) | HWPX file parsing/packaging (`HwpxPackage`) | MIT |
| [hwp2hwpx](https://github.com/neolord0/hwp2hwpx) by neolord0 | HWP → HWPX binary conversion (Java) | Apache 2.0 |
| [@ohah/hwpjs](https://github.com/niceoasi/hwpjs) by ohah | HWP → JSON/Markdown/HTML (Node.js) | MIT |
| [lxml](https://lxml.de/) | HWPX XML parsing and manipulation | BSD |
| [python-dotenv](https://github.com/theskumar/python-dotenv) | `.env` environment-variable loading | BSD |
| [pypdf](https://github.com/py-pdf/pypdf) | PDF text extraction | BSD |

Reference & inspiration: [hwp-pipeline (Yoojin-nam)](https://github.com/Yoojin-nam/hwp-pipeline) — the Claude Code skill whose early design informed this project · the HWP/HWPX format specification (Hancom HWPML 2011/2016) · [Claude Code](https://docs.anthropic.com/en/docs/claude-code) by Anthropic
