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

5.5 KiB

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).