# DOX — packages/document-converter

Files in this directory. One row per source file.

| File | Purpose |
|------|---------|
| `README.md` | Package overview. TS facade over Dockerized Python engine `pi-doc-engine`. Only doc-conversion surface; callers never touch Python/docling/pandoc/nano-banana. Ingest PDF/DOCX/PPTX/XLSX → provenance-stamped Markdown (feeds kb). Produce Markdown → templated DOCX/PDF, diagrams, round-trip edit/merge. Export `createDocumentConverter`. Engine Docker-quarantined. |
| `engine/README.md` | pi-doc-engine Docker image wrapping vendored Python engine. TS facade only caller, no host Python. Build `./build-image.sh` → `pi-doc-engine:$(cat IMAGE_VERSION)`; `IMAGE_TAG=… ./build-image.sh`. Build guard fails on vendored `*.py` `~/Documents` path refs. Invocation contract: one JSON request stdin, one JSON response stdout, exit-code signals success. |
| `engine/VENDOR.md` | Vendored engine provenance record. Committed copy of Python doc-processing engine; `pi-doc-engine` image builds only from copy. Sources table: vendored path, upstream source, version, copy date, anchor-file sha256. Refresh by re-copy from upstream + update record. Upstream not under git → sha256 + date establish traceability. |
| `engine/document_converter/cli.py` | Vendored engine argparse CLI. Subcommands `convert`, `extract`, `merge`, `create-template`, `analyze`, `list-templates`, `convert-pdf`. Exports `build_parser()` (split out of `main()` so tests parse argv without executing) and `resolve_pdf_output(inputs, output, output_dir)`. `convert-pdf` output is the `-o/--output` FLAG; `inputs` is `nargs='+'` and swallows a positional output, so the documented `convert-pdf in.docx out.pdf` form is recovered by `resolve_pdf_output` (last positional, `.pdf`, not existing, no `--output`/`--output-dir`, ≥2 positionals). See issue #507. |
| `engine/document_converter/cover_page.py` | Cover-page generation + template cover copy. Exports `copy_template_cover_page`, `add_cover_page`, `extract_title_and_description`, `demote_headings`, `normalize_heading_levels`, `remap_relationship_ids`, `find_dangling_relationship_ids`. Copied template XML carries TEMPLATE-local `r:id`s while pandoc numbered the output's rels independently, so `copy_template_cover_page` remaps every `r:*` id BY TARGET (never by number — that repoints `type="first"` at footer3), DROPS a header/footer reference the output cannot satisfy, and raises rather than writing a package with a dangling id. See issue #506. |
| `engine/engine_cli.py` | JSON-over-stdio entry point of the Docker image. Maps one command per request (`renderDocx`, `renderPdf`, `extractForEdit`, `mergeBack`, `fillFrontmatter`, `profileTables`, ingest) onto `document_converter.cli` subprocesses via `_run_dc`. `cmd_render_pdf` passes `--output`, never a positional. See issue #507. |
| `engine/tests/test_convert_pdf_output.py` | Pytest regression for `convert-pdf` output resolution (#507). Round-trips `cmd_render_pdf` argv through the real `build_parser()`; pins that a batch never loses its last input. Stdlib + monkeypatch, no docling/Docker. Run `python -m pytest packages/document-converter/engine/tests -q`. |
| `engine/tests/test_cover_page_relationships.py` | Pytest regression for cover-page relationship remapping (#506). Builds a template whose page break sits in the sectPr-carrying paragraph (else the assertions go vacuous) + an output package renumbered into a disjoint rId range. Pins: no dangling ids, references keep their target, unresolvable header dropped, dangling non-section ref raises. python-docx only, no pandoc/Docker. |
| `vitest.config.ts` | Vitest config. include `src/**/__tests__/**/*.test.ts`. node env, forks pool, maxWorkers 50%. |
| `.pi/skills/document-converter/SKILL.md` | NL-triggered document conversion. Ingest PDF/DOCX/PPTX/XLSX→md for kb; produce md→DOCX/PDF templated. Routes to packages/document-converter facade. See change: document-converter. |
