Assume the reader knows nothing about this project. This file lists invariants, entry points, and easy mistakes. Everything else is in the linked modules and docs—open those when you change behavior. Entry points (area, role, paths) are in Key files at the bottom.
Important
Docs: After any nontrivial change, update documentation. Prefer the topic doc under docs/; touch AGENTS.md only when the change affects many areas or global rules.
[!IMPORTANT]
Complexity: This codebase is complicated for its size. When asked to do a new feature, always figure out the way using the least amount of code or extra complexity. Using existing functions, there are many functions which can just be used or refactored to make the change small for a new feature.
If you find ways to lower technical debt, while adding a feature, put that in your plan.
Important
Tests: New features and bugfixes must include tests.
- Unit:
tests/, pytest when logic can be mocked. Test files should match the source module name (e.g.foo.py->test_foo.py). Always add new test cases to the matchingtest_file to maintain consistent naming and visible coverage. - UNO / LibreOffice:
tests/uno/or_uno.pysuffix viatesting_runner.py(no pytest)—use@native_test,@setup,@teardown; test functions takectx. Follow the same module-matching rule (e.g.foo.py->test_foo_uno.py). - Execution Policy: Run tests for the specific files modified plus
make typecheck. Run fullmake testONLY IF making large refactors or cross-cutting changes.
Important
Comments: Write why this code is there for the reader who would otherwise be lost. Good comments are the bridge from opaque to understandable and maintainable code. Some files have no comments: inserting footnotes is standard, little different from other UNO objects. Meanwhile some comments are critical to understanding why the code is there. Write clear, short comments.
- Bugfixes (required): at the fix, what was wrong, how it happened, and why this change fixes it.
- LibreOffice / UNO / Etc.: quirks. When matching upstream behavior, cite source (file + line or function), not a vague “like Lightproof.”
WriterAgent is a LibreOffice extension (Python + UNO) for Writer, Calc, and Draw (Impress paths where registered).
- Chat: Sidebar + menu chat (Writer/Calc deck; Draw per code paths)—multi-turn, tools, history (SQLite when available, else JSON under
writeragent_history.db.d/). - Extend / Edit selection: Writer uses
get_string_without_tracked_deletions()for prompts; undo/session details indocument_helpers. - Settings:
writeragent.jsonunder the LibreOffice user profile—seeconfigmodule doc. - Memory (experimental):
memory+MEMORY_GUIDANCEinprompts— docs/hermes-agent-patterns.md. - Calc:
=PROMPT()and=PYTHON()add-ins (see Key files). - Eval / benchmarks:
make run_eval/scripts/benchmark.py→scripts/prompt_optimization/— scripts/prompt_optimization/README.md, docs/eval-dev-plan.md.
Python: Dev/tooling 3.11–3.13 (pyproject.toml); dev .venv is pinned to 3.13 via .python-version (3.14 lacks wheels for some dev deps such as spaCy). Extension runtime is whatever LibreOffice bundles (often older). Shipped code under plugin/ must not rely on stdlib newer than that runtime.
GPL v3+; prior contributors credited in headers/installer.
| Command | When to use |
|---|---|
make typecheck |
After edits (required with targeted tests). Checker details: docs/type-checking.md |
make deploy |
Build + install/cache sync; restart LibreOffice (or make deploy writer/calc/draw/impress to launch) |
make test |
Large or cross-cutting changes only (includes typecheck, SAST, pytest, LO tests) |
make build |
Produce build/WriterAgent.oxt only (no install) |
Usual targets generate plugin/_manifest.py when needed. Other Makefile targets exist for release, fuzz, and niche tooling—see the Makefile when you need them.
Chat and tool calls go through llm_client (see its module doc). Persistent connections live in ai/service; auth headers in auth.
The librarian / smolagents path must use WriterAgentSmolModel in smol_agent—do not add a second HTTP client. Details: docs/smol-main-chat-tool-architecture.md, docs/llm-hacks.md.
Rules that apply in many places. Breaking them causes wrong-document bugs, frozen UI, or tools that never run. Paths are in Key files.
-
Use the extension’s
self.ctx, not a fresh UNO context. Lookups for package info, dialogs, and similar must use the component context the extension was given. Callinguno.getComponentContext()can return a different context and quietly break those lookups. Same idea for Calc chat context:get_calc_context_for_chatneedsctxfrom the panel / MainJob, not a bootstrap call. -
Keep the chat FSM pure. In
service,next_stateonly computes the next state—no UNO calls and no I/O. Side effects (UI updates, MCP, document work) belong in the panel or MCP layers. -
Stream on a worker; drain on the UI thread. Background work pushes tuples onto a
queue.Queue. The first element must be aStreamQueueKindenum member, not a bare string. Drain withrun_async_worker_with_drain/get_toolkit(ctx)so the UI processes events viatoolkit.processEventsToIdle(). Do not use UNOXTimerListenerfor sidebar streaming. More: docs/streaming-and-threading.md. -
Refresh document context each chat send. Each user send replaces the
[DOCUMENT CONTENT]system message so the model sees the current document, not a stale snapshot. -
Register tools so schemas and execution agree. Matching uses
uno_servicesfirst, thendoc_types. Anything advertised byget_schemasmust be runnable viaexecute. Default main-chat tools aretier="core"; nested specialized sets usespecialized/specialized_controland are omitted from default lists. Gateway tools must list every UNO service they support (e.g. Draw and Impress). Writercharts/shapesshare tool names with Calc/Draw—the Writer class must declare the union of those services or execution rejects the document. -
Do not start raw threads for background work. Use
run_in_background. Long subprocesses useAsyncProcess; if stderr is piped, drain it continuously or redirect it, or the process can deadlock (docs/reentrancy-and-ipc-deadlock-prevention-plan.md). Dev builds enable a UNO thread guard by default (thread_guard; setWRITERAGENT_UNO_THREAD_GUARD=0to opt out; release OXTs stub it off). Wrap document-model access at boundaries withguard_uno(e.g.get_active_document, frame_get_document_model,resolve_document_by_url,open_document_for_read). ForToolContext, useget_ctx()—not the raw bootstrapself.ctx. Details: docs/uno-thread-safety-enforcement.md. -
Surface errors through the shared helpers. Prefer
WriterAgentExceptionandformat_error_payload(errors). Tools should fail via_tool_error. There is no activeDocumentCache—do not assume one.
UNO helpers are intentionally split (uno_context, document_helpers, dialogs)—there is no monolithic uno_helpers.py.
Area-specific rules live in module docstrings and topic docs—open those when you edit that area. Entry points: Key files. Topic docs: Deep dives.
-
Sidebar / chat: Resolve the document from the frame only (
frame.getController().getModel()inpanel). For Stop / cancel, useresolve_stop_checker()—not a panel boolean alone. Modes and routing: docs/chat-sidebar-implementation.md. Streaming details: docs/streaming-and-threading.md. -
Dialogs (XDL): Load with
DialogProviderand the extensionbase_url(seedialogsmodule doc). Settings UI is indialog_views. -
Tools / Writer / Calc: In tests, resolve tools with
plugin.main.get_tools().get("tool_name"). Deeper topics: docs/math-tex.md, docs/realtime-grammar-checker-plan.md, docs/calc-specialized-toolsets.md, docs/enabling_numpy_in_libreoffice.md, docs/calc-py-data-shapes.md, docs/numpy-domains.md. -
Config: Call
init_config(ctx)once at bootstrap. Later config I/O does not takectx—see theconfigmodule doc. -
Logging / MCP: Logs go to
writeragent_debug.lognext towriteragent.json. Shipped LibrePy defaults tolog_levelWARN; a checkout that still hasplugin/tests/defaults to DEBUG. Override inwriteragent.jsonand restart LibreOffice.enable_agent_logis separate (structured agent traces only). In unexpectedexceptblocks, uselog.exception("Context"). MCP work drains on the main thread (docs/mcp-protocol.md). Image generation: docs/image-generation.md. Do not read API keys from the environment in production; do not usetempfile.mktemp(). For scratch debug files under/tmp, preferflush=True. -
Tests / packaging: UNO tests go through
testing_runner; debug-menu suites run on the UI thread (docs/test_architecture_analysis.md). New extension components must be registered inextension/META-INF/manifest.xml.
Do not reuse the names logging, module log, or gettext _ for unrelated variables. UI code imports _ from i18n. Never bind bare _ as a throwaway (for _ in …, a, _, _ = fn(), except Exception as _:)—use a real name (unused, idx). Private helpers named _foo are fine.
Start here by task. Topic docs: Deep dives.
Layout: plugin/ (framework, chatbot, writer, calc, draw, scripting, …), extension/ (OXT resources, Dialogs, idl, metadata), scripts/, Makefile, pyproject.toml.
| Area | Role | Paths |
|---|---|---|
| Bootstrap / MCP | Extension bootstrap, settings apply, MCP startup | plugin/main.py |
| Sidebar / send | Sidebar factory, panel, document resolution | plugin/chatbot/panel_factory.py, plugin/chatbot/panel.py |
| Tool loop / chat FSM | Main chat tool loop and state machine | plugin/chatbot/tool_loop.py, plugin/chatbot/tool_loop_state.py |
| Smol / librarian ReAct | Separate ReAct runtime (shares LlmClient); do not merge with the main chat FSM |
plugin/chatbot/smol_agent.py — docs/smol-main-chat-tool-architecture.md |
| Agent backends | Optional external backends (agent_backend.backend_id when not builtin) |
plugin/agent_backend/ |
| HTTP / LLM | Chat requests, tools, token stripping, pacing | plugin/framework/client/llm_client.py (make_chat_request, request_with_tools, …), plugin/ai/service.py, plugin/framework/client/auth.py |
| Tools registry | Tool registration and schemas | plugin/framework/tool.py |
| UNO document helpers | Document open/resolve, undo, selection helpers | plugin/doc/document_helpers.py |
| Config / keys / LRU | writeragent.json, keys, LRU |
plugin/framework/config.py |
| Dialogs / XDL | Dialog load helpers and settings UI | plugin/chatbot/dialogs.py, plugin/chatbot/dialog_views.py, plugin/chatbot/settings_dialog.py |
| Async UI drain | Stream queue drain on the UI thread (get_toolkit, get_ctx) |
plugin/framework/async_stream.py, plugin/framework/uno_context.py |
| Writer HTML / apply | HTML import and apply-content paths | plugin/writer/format_support.py |
| Writer charts / shapes | Shared tool names with Calc/Draw; declare union of uno_services |
plugin/writer/charts.py, plugin/writer/shapes.py |
| Errors | WriterAgentException, safe_json_loads, tool errors |
plugin/framework/errors.py |
| FSM / service | Pure next_state only; no UNO/I/O in transitions |
plugin/framework/service.py |
| Threading / UNO guard | run_in_background, AsyncProcess, Layer A guard_uno |
plugin/framework/worker_pool.py, plugin/framework/thread_guard.py |
| UNO listeners / i18n | UNO listeners; gettext _ for UI |
plugin/framework/uno_listeners.py, plugin/framework/i18n.py |
| Memory / prompts | Experimental memory + MEMORY_GUIDANCE |
plugin/chatbot/memory.py, plugin/framework/prompts.py |
| Extension update check | Weekly WriterAgent / LibrePy / LibreHarper update check | plugin/chatbot/extension_update_check.py |
Calc =PROMPT() / =PYTHON() |
Calc spreadsheet function add-ins | plugin/calc/prompt_addin.py, plugin/calc/prompt_function.py, plugin/calc/python/addin.py, plugin/calc/python/function.py |
| Scripting / venv | Public script API, sandbox policy, venv worker (not for user imports) | plugin/scripting/, plugin/scripting/venv/, plugin/scripting/import_policy.py, plugin/scripting/sandbox.py, plugin/scripting/venv_worker.py, plugin/scripting/venv_diagnostics.py |
| Embeddings / folder FTS | Host indexers + venv worker + RPC | plugin/embeddings/, plugin/embeddings/venv/, plugin/framework/client/embeddings_service.py, plugin/framework/client/embedding_client.py, plugin/framework/client/folder_fts_service.py — docs/embeddings.md |
| Vision / OCR | Host runner + venv worker + run_vision |
plugin/vision/, plugin/vision/venv/, plugin/scripting/client.py, plugin/vision/vision_availability.py — docs/image-recognition.md |
| PPT-Master | Impress/Draw adapters and session | plugin/contrib/ppt_master/ (README), plugin/ppt_master/, plugin/chatbot/ppt_master.py — integration plan |
| Tests (UNO runner) | Native UNO tests (@native_test, ctx) |
plugin/testing_runner.py |
| Eval / benchmarks | CLI eval harness and prompt optimization | scripts/benchmark.py, scripts/prompt_optimization/ |
| Extension packaging | OXT resources; register new components in manifest | extension/ (Dialogs/, idl/, metadata/), extension/META-INF/manifest.xml |
| Build / tooling | Make targets, package metadata, Python pin | Makefile, pyproject.toml, .python-version |
- Dialog DTD (LibreOffice tree):
xmlscript/dtd/dialog.dtd - GUI DevGuide: https://wiki.documentfoundation.org/Documentation/DevGuide/Graphical_User_Interfaces