VibeCody uses your installed LibreOffice as a document engine: it opens Visio diagrams and legacy Office files, and renders documents to PDF, PNG or SVG. It is optional and needs no setup. If LibreOffice is not installed, nothing breaks — those files show a message saying what to do.
What it is for, and what it is not
| Use | Through LibreOffice |
|---|---|
View a Visio diagram (.vsd, .vsdx, .vsdm, stencils and templates) |
✅ read-only, via libvisio |
View legacy .doc, .xls, .ppt and ODF (.odt, .ods, .odp, .odg) |
✅ read-only |
| Export any of those to PDF, PNG, SVG, DOCX, XLSX, PPTX, ODF, HTML, TXT, CSV | ✅ a copy is saved; the original is untouched |
| Edit a file | ❌ not here |
| Write a Visio file | ❌ LibreOffice reads Visio but cannot save it |
It is a converter, not an editor. A LibreOffice round trip regenerates the document, which drops formatting it does not model. In a controlled test, converting a styled DOCX through a regenerating engine lost the text colours, table shading and image; the surgical writers in vibe-docfmt, vibe-sheetfmt and vibe-slidefmt kept them. So DOCX, XLSX, PPTX, Pages, Numbers and Keynote keep their existing in-process viewers and “Edit as Markdown” paths — LibreOffice is used for what those do not cover.
Zero-config
Nothing to set. LibreOffice is found, in order, at:
- Your override (Settings → Office), if you set one — and only that. A path that does not work is reported, never silently replaced by a different LibreOffice.
sofficeorlibreofficeonPATH.- Standard install locations —
/Applications/LibreOffice.app(macOS),%ProgramFiles%\LibreOffice(Windows),/usr/bin/soffice,/opt/libreoffice*,/snap/bin/libreoffice(Linux). A Finder- or Start-menu-launched app has a minimalPATH, so these are probed directly.
The result is visible in three places:
| Where | What you see |
|---|---|
| Daemon startup banner | office: LibreOffice ready — LibreOffice 26.8… (/Applications/…/soffice) or office: LibreOffice not found — … |
GET /health → features.soffice |
state: checking · ready · missing · configured_invalid · broken, plus version, path, source |
| Settings → Office | The same verdict, a path override field, Re-check and Auto-detect |
checking means the first probe has not finished (a cold LibreOffice takes seconds to report its version); it is never reported as missing.
Setting a path
Only needed if LibreOffice is somewhere unusual. Settings → Office → Path to soffice, or:
curl -X PUT http://localhost:7878/office/soffice/path \
-H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"path": "/opt/lo/program/soffice"}'
The value is stored in the encrypted ProfileStore (~/.vibecli/profile_settings.db, key office.soffice_path) — not in config.toml, not in an environment variable. The daemon and the desktop app read the same key. A path that cannot start LibreOffice is refused (422) and the previous setting is kept, so Settings never holds a value that silently turns the feature off. Send {"path": null} to go back to auto-detection.
How conversion behaves
- A private profile. Conversions run with
-env:UserInstallationpointing at~/.vibecli/office/profile-<host>, never your own LibreOffice profile. Sharing yours makes a headless run hand off to a LibreOffice window you have open and exit having done nothing. - One at a time per host. LibreOffice allows one instance per profile, so conversions in a process are queued. The daemon and the desktop app each have their own profile.
- The exit code is not trusted. LibreOffice exits
0having written nothing. A conversion succeeds only if a non-empty file of the right kind (checked by its magic bytes) exists. - Inputs are checked first. A file whose bytes do not match its extension — a
broken.docxthat is not a ZIP — is refused (422) before LibreOffice runs, because LibreOffice would import it as plain text and hand back a valid-looking PDF of garbage. Legacy binary formats are not policed this way: an.xlsthat is really HTML or CSV is legitimate. - Cached. Results are keyed on the file’s path, modification time and size, the target, and the LibreOffice version, under
~/.vibecli/office/cache(newest 100 kept). An unchanged file does not start LibreOffice again; the response says"cached": true. - Bounded. Inputs over 200 MB are refused; a conversion that exceeds its time limit (120 s by default) is stopped along with its child processes, so a hung run does not hold the profile lock for every later one.
- PNG and SVG are the first page or slide only. Use PDF for anything multi-page.
- Spreadsheets have their own PDF target,
pdf_sheets: one page per sheet, sized to the sheet. The ordinary PDF paginates a sheet and clips its columns (a 40-column CSV came out as 13 pages; withpdf_sheetsit is 1). It is refused (422) for anything that is not a spreadsheet. It is capped by the PDF page size (about 14,400 pt, ~1,100 rows): beyond that LibreOffice does not split the sheet, it shrinks the whole sheet to fit — 20,000 rows came out 23 pt wide and unreadable. The app therefore offers the accurate spreadsheet view only while every sheet has at most 1,000 rows and 200 columns.
macOS: Gatekeeper and LibreOffice’s signature
Some people have to approve LibreOffice in System Settings → Privacy & Security before it will run. There are two separate causes, and VibeCody addresses only one of them.
- LibreOffice changes its own app. Its embedded Python writes bytecode (
.pyc) files insideLibreOffice.app, including files the code signature covers. macOS then reportsa sealed resource is missing or invalid(codesign --verify --deep --strict /Applications/LibreOffice.appshowsfile modified: …/__pycache__/….pyc), Gatekeeper rejects the app, and you are asked to allow it. This is LibreOffice’s behaviour, not VibeCody’s: on one machine the first such file was written five minutes after LibreOffice was installed. Opening LibreOffice yourself does the same thing, and VibeCody cannot prevent that. - VibeCody must not add to it. A headless conversion with a fresh profile was measured writing 31
.pycfiles into the app bundle (0 with the setting below). VibeCody now runs every LibreOffice process withPYTHONPYCACHEPREFIXpointing into its own private profile (~/.vibecli/office/profile-<host>/pycache), so its conversions leave the signed app untouched. A one-time cost: Python compiles its own bytecode on the first run instead of using the files shipped in the app. A test checks the bundle’s.pycfiles are unchanged after a conversion.
What VibeCody does not do: it never clears the quarantine flag (com.apple.quarantine), approves the app, or changes any Gatekeeper setting for you. Whether to trust that app is your decision.
What you see when macOS blocks it: if LibreOffice fails to start (or does not answer, which is how a Gatekeeper prompt waiting on you looks) and spctl --assess says the app is rejected, Settings → Office, the startup banner and the viewer say so, with the setting to open: Privacy & Security → the message about LibreOffice → Open Anyway, then Re-check.
Repairing an install that is already unsealed: VibeCody does not modify /Applications/LibreOffice.app. Its new behaviour does not repair an existing broken signature; reinstalling LibreOffice from libreoffice.org gives a sealed copy, which stays sealed as long as only VibeCody runs it.
HTTP
All routes require the bearer token (daemonFetch in the panels).
| Route | Purpose |
|---|---|
GET /office/soffice |
Fresh status — same body as /health.features.soffice, plus import_extensions |
PUT /office/soffice/path |
Set or clear the override ({"path": "…"} / {"path": null}) |
POST /office/convert |
{"path": "…", "to": "pdf" \| "pdf_sheets" \| "png" \| …, "timeout_secs": 120} → {output, target, bytes, cached, engine_version, elapsed_ms} |
POST /office/convert refuses credential-store paths (403) and reads only the file named; the result lands in the daemon’s cache and output is its local path (a remote client cannot fetch the bytes through this route yet).
| Status | Meaning |
|---|---|
503 |
LibreOffice not available — the body carries soffice with the state and remedy |
404 |
Input file not found |
422 |
Unsupported extension, or the bytes are not that format |
413 |
Input over 200 MB |
502 |
LibreOffice ran but produced nothing usable, or exited non-zero |
504 |
Timed out and was stopped |
In the app
Opening a Visio, .doc, .xls, .ppt or ODF file in VibeCoder renders it through LibreOffice and shows it in the PDF viewer, labelled Rendered by LibreOffice · Read-only. Files that have their own editable viewer get the same rendering as an option:
| File | Accurate view | Default? |
|---|---|---|
.pptx |
Accurate view / Edit shapes in the deck toolbar | Yes — the shape view does not draw layout graphics or photos |
.docx |
Accurate beside Word view / Text view; also offered from the notice when the file has legacy VML shapes the Word view cannot draw | No |
.xlsx, .xlsm, .csv |
Accurate view in the grid toolbar (one page per sheet; see pdf_sheets above) |
No — the grid is editable. Disabled for a sheet over 1,000 rows or 200 columns, and while there are unsaved edits (it renders the file on disk) |
Export as… converts to another format and saves a copy through a save dialog. Failures are specific: not installed points at Settings → Office, not a valid .vsdx says the file is the problem, timed out says LibreOffice was stopped.
Known limits
- Rendering fidelity is LibreOffice’s, not Microsoft’s. Complex Visio diagrams (custom masters, layers, some connectors) may differ from what Visio shows. A hand-built minimal
.vsdxrenders correctly; real-world diagrams have not been compared shape-for-shape. - Apple formats (
.pages,.numbers,.key) are attempted only if your LibreOffice build includes libetonyek; the native viewers remain the primary path for them. - Only VibeCoder has the viewer; VibeDesk and VibeAIChat show the Settings section and can call the routes.