Rentenversicherer/PLAN.md
Kenearos 3c669fb003 feat: AcroForm-Fill via Claude CLI, Multi-Source, Kanagawa, Docker-Deploy
Komplettes Rework der AI-Studio-Vorlage zu einem produktiven Werkzeug fuer
deutsche AcroForm-Formulare (Reha-Antraege, Arzt-Befundberichte):

- Backend: Express spawnt headless Claude CLI ('claude -p --output-format json'
  via stdin-Pipe). Prompt enthaelt die Feldnamen als Ziel-Schema plus die
  Arbeitsregeln (Stichwortstil, feste Zeichen-Kaestchen ohne Leerzeichen,
  Vordrucke respektieren, keine geratenen Werte, nur medizinisch).
- PDF-Handling: pdfjs-dist statt pdf-lib — pdf-lib scheitert an verschluesselten
  Object-Streams in DRV-Formularen. annotationStorage + saveDocument, kein
  Flatten. Worker-Patch zur Laufzeit forciert Auto-Size und schwarze Schrift.
- Multi-Source-Upload: beliebig viele PDFs/Bilder + optional Freitext.
- Design: Kanagawa Design System (Preset aus ../kanagawa-design-system),
  Tailwind lokal gebaut statt CDN, Dark/Light-Toggle, Progress-Indicator.
- Deployment: Multi-Stage-Dockerfile, docker-compose in matrix_default-Netz,
  Claude-Credentials vom Host per Volume. PLAN.md + AGENTS.md (Alex-Schema).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:48:32 +02:00

135 lines
5.5 KiB
Markdown

# Rentenversicherer: AcroForm Auto-Fill
Purpose
- Browser-UI nimmt ein Original-PDF (mit AcroForm-Feldern) und ein
Quelldokument (Scan, Brief, Ausweis, o.ä.) entgegen.
- Ein lokales Node-Backend ruft die Claude Code CLI (`claude -p`) als
Subprozess auf, übergibt beide Dateien und die Liste der AcroForm-
Feldnamen. Claude liefert strukturiertes JSON mit `{feldname -> wert}`.
- User reviewt/korrigiert die Werte im Browser, lädt das ausgefüllte
PDF runter.
- Das heruntergeladene PDF bleibt AcroForm (keine Flattening-Operation).
Im PDF-Reader nachträglich manuell editierbar — als hätte ein Mensch
es ausgefüllt.
## Scope
- Frontend: React/Vite, Upload + Review-Panel + Live-Preview + Download.
- Backend: Minimaler Node-Server, eine Route `POST /api/process`.
Spawnt `claude -p --output-format json` mit Temp-Files und einem
Prompt, der die Ziel-Feldnamen enthält.
- PDF-Handling: `pdfjs-dist` client-side — Widgets sammeln, Werte via
`annotationStorage.setValue(id, { value })` setzen, `doc.saveDocument()`
schreibt ein PDF ohne Flatten. (pdf-lib wurde getestet, scheitert aber
an komprimierten Object-Streams in DRV-Formularen.)
- Lokaler Single-User-Betrieb (localhost).
## Out of scope
- Gemini, Anthropic SDK, Claude Agent SDK — nur die CLI als Subprozess.
- Visual-Overlay-Modus mit Koordinaten für gescannte Flat-PDFs.
- Form-Overlay-View (Drag & Drop) im Review-Panel.
- Authentifizierung, Multi-User, Persistenz, Datenbank.
- PDF-Flattening, Signaturen, Formular-Editor, Seiten-Modifikation.
- Cloud-Deployment.
## Architektur
```
Browser (Vite :5173)
│ multipart upload: form.pdf + source.pdf + fieldNames[]
Node Server (:3001, Express)
│ 1. schreibt beide PDFs in Temp-Verzeichnis
│ 2. spawnt:
│ claude -p --output-format json \
│ --permission-mode bypassPermissions \
│ "<Prompt mit Feldliste + Pfaden>"
│ 3. parst stdout → extrahiert JSON aus result-Feld
│ 4. löscht Temp-Verzeichnis
Browser bekommt { fields: [...], summary: "..." }
│ User reviewt/korrigiert
│ pdfjs-dist: annotationStorage.setValue + saveDocument
│ (kein flatten; AcroForm bleibt erhalten)
Download
```
## Repo-Layout
```
Rentenversicherer/
├── App.tsx # Upload → Process → Review Flow
├── components/
│ ├── FileUpload.tsx
│ └── ReviewPanel.tsx # nur List-View, Overlay-View entfernt
├── services/
│ ├── api.ts # Frontend-Client für /api/process
│ └── pdfService.ts # getPdfFields + createFilledPdf via pdfjs-dist
├── server/
│ ├── index.ts # Express: static + /api/process
│ └── claudeRunner.ts # spawn('claude', ['-p', ...]); JSON-Extraktion
├── types.ts
├── vite.config.ts # dev-proxy /api → :3001
├── package.json # dev-script: vite + server parallel
├── tsconfig.json # shared tsconfig für Client + Server
├── .gitignore
├── PLAN.md
├── AGENTS.md
└── README.md
```
## Workflow (User-Sicht)
1. Original-PDF hochladen. Client scannt AcroForm-Feldnamen
mit `getPdfFields()` und zeigt die Anzahl als Hinweis.
2. Quelldokument hochladen (PDF oder Bild).
3. "Analyze & Fill" klicken.
4. Browser sendet beide Dateien + Feldliste an `POST /api/process`.
5. Server schreibt Dateien ins Temp-Verzeichnis, baut Prompt, spawnt
Claude CLI, parst JSON, antwortet.
6. Review-Panel zeigt `{label, value, validation}`-Paare — User korrigiert,
markiert als verified.
7. "Download" → `createFilledPdf` setzt die Werte via pdfjs
`annotationStorage` und schreibt mit `doc.saveDocument()`.
**Kein** Flatten.
8. User öffnet das runtergeladene PDF → Felder sind gefüllt und
bleiben klick- und editierbar in Acrobat/Reader.
## Umsetzungsschritte
1. `package.json`: Server-Deps (`express`, `multer`, `tsx`,
`concurrently`, typings) ergänzen, Scripts aktualisieren.
2. `server/claudeRunner.ts`: `spawn` mit Windows-kompatiblem Aufruf,
stdout buffern, `--output-format json` Result-Wrapper entpacken,
inneres JSON parsen.
3. `server/index.ts`: Express-App, `multer` für Upload, Temp-Dir pro
Request, `finally`-Cleanup.
4. `vite.config.ts`: `server.proxy['/api']` nach `http://localhost:3001`.
5. `services/api.ts` als Frontend-Client; `services/geminiService.ts`
löschen.
6. `services/pdfService.ts`: auf `pdfjs-dist` umgestellt.
`getPdfFields` iteriert über Pages + Widget-Annotations,
`createFilledPdf` nutzt `annotationStorage` + `saveDocument`.
7. `components/ReviewPanel.tsx`: Form-Overlay-View entfernen, nur
List-View behalten, Koordinaten-Logik raus.
8. `App.tsx`: Fehlermeldung, falls Ziel-PDF keine AcroForm-Felder hat
(UI blockiert Upload dann).
9. Smoke-Test mit
`G2210-11_Aerztlicher_Befundbericht_Anforderung_WAG.pdf`:
`npm run dev`, ausfüllen, runterladen, in Acrobat öffnen,
Feld editieren können.
## Notes
- Claude CLI wird über dein bestehendes Login authentifiziert. Kein
API-Key im Code oder in Env-Dateien.
- `claude -p` braucht `--permission-mode bypassPermissions`, damit das
Read-Tool ohne User-Prompt auf die Temp-Files zugreifen darf.
- Temp-Verzeichnisse liegen unter `os.tmpdir()` und werden nach jedem
Request gelöscht. Kein persistenter Upload-Ordner.
- Windows-Pfad-Konvention: Server nutzt `path.join`, arbeitet also
plattformneutral. Der Claude-CLI-Aufruf läuft via `shell: true` unter
PowerShell (vgl. globale CLAUDE.md).