Pi4Word is a Microsoft Word task pane add-in that embeds a Pi Agent assistant powered by @mariozechner/pi-agent-core.
The assistant can read the current Word selection as GitHub-flavored Markdown, reason over the document through Word tools, and insert or replace content by rendering Markdown into sanitized HTML for the Word JavaScript API.
Core user-visible surfaces:
@mariozechner/pi-web-ui ChatPanel, mounted into #chatMount.Range.getHtml() -> Turndown + turndown-plugin-gfm, with text fallback.marked -> DOMPurify -> Word insertHtml.src/index.js imports ./shims/office-alert.js first, then pi-web-ui CSS, src/index.css, and initializeTaskPane.initializeTaskPane() calls scheduleOfficeBoot(), which waits for DOMContentLoaded if needed, then Office.onReady.Office.onReady is unavailable or does not complete within 2.5s, the UI still mounts with missing host context.renderApp() clears #app-root, builds the header, toolbar, and #chatMount.src/index.js installs window error and unhandledrejection handlers that surface boot failures in #app-root.public/index.html provides #app-root, loads Office.js from https://appsforoffice.microsoft.com/lib/1/hosted/office.js, then loads ./index.min.js as an ES module and ./index.min.css in head.manifest.xml targets Word and points SourceLocation to https://localhost:3000/public/index.html.SYSTEM_PROMPT lives in src/assistant/pi-assistant.js.createWordAgent() and createWordAgentFromSession() create pi-agent-core Agent instances using defaultConvertToLlm.getDefaultWordModel() uses DEFAULT_SETTINGS from settings.getAppStorage().providerKeys.mountChatPanel() creates new ChatPanel(), appends it to #chatMount, and calls chatPanel.setAgent().toolsFactory returns ...createWordTools() first, then createPi4WordJavaScriptReplTool().registerCollapsibleWordToolRenderers() registers collapsible renderers for the Word tool names.ModelSelector updates agent.state.model and persists the preferred model.ApiKeyPromptDialog.prompt(provider) handles missing provider keys.Tool descriptions and parameter schemas in source are authoritative.
createWordTools() lives in src/assistant-tools/index.js and returns these Word tools in order.
| Tool | Source | Contract |
|---|---|---|
word_get_selection |
word-tool-get-selection.js |
Runs Word.run, loads selection text, queues range.getHtml(), syncs, converts Word HTML to GFM Markdown, and falls back to range.text if Markdown is empty but text is present. |
word_get_document_outline |
word-tool-get-document-outline.js |
Returns a table-of-contents-style outline from document body paragraphs using built-in Heading 1-9 styles and/or outline levels 1-9. |
word_search_text |
word-tool-search-text.js |
Searches the document body with Word search, accepts optional search_options, and returns match count plus a preview for the selected match_index. |
word_insert_markdown |
word-tool-insert-markdown.js |
Converts Markdown to sanitized HTML and inserts it using Word insertHtml at the requested placement or search anchor. |
word_insert_markdownRequired parameters:
markdown: Markdown to render and insert. Plain prose counts as Markdown.where: after_selection, before_selection, replace_selection, or end_of_document.Accepted where aliases:
after -> after_selectionbefore -> before_selectionreplace -> replace_selectionend / document_end -> end_of_documentOptional search anchoring parameters:
anchor_search_textanchor_match_indexanchor_search_optionsWhen anchor_search_text is non-empty and where is not end_of_document, insertion targets the Nth body search hit instead of the current selection.
word_search_text.search_options and word_insert_markdown.anchor_search_options accept Word SearchOptions flags in snake_case:
ignore_punctignore_spacematch_casematch_prefixmatch_suffixmatch_whole_wordmatch_wildcardsWhen an insert is anchored from a prior search, anchor_search_options must mirror the original search_options; mismatched flags can target a different occurrence. Word wildcard and search behavior follows Microsoft’s search-option guidance.
marked and DOMPurify.<input type="checkbox"> is allowlisted for insertion.createPi4WordJavaScriptReplTool() lives in src/assistant-tools/javascript-repl-tool.js and wraps pi-web-ui’s javascript_repl.
Contract differences from the upstream tool:
title is required.code is accepted.script is accepted as an alias for code.code or script must be a non-empty string.initPiWebStorage() creates pi-web-ui AppStorage with IndexedDB-backed settings, provider keys, sessions, and custom providers.migrateLegacyLocalStorageOnce() may migrate pi4word.settings.v1 from localStorage into provider keys, preferred model keys, and Pi4Word stream proxy settings.pi4word.chat.preferredProvider and pi4word.chat.preferredModelId.pi4word.chat.preferredThinkingLevel and mirrored to localStorage as a synchronous embedded-host fallback/cache because IndexedDB writes may not finish before unload.proxy.enabled and proxy.url.pi4word.streamProxy.enabled, pi4word.streamProxy.url, and pi4word.streamProxy.token.LLM traffic uses createPi4WordStreamFn() in src/assistant/pi-assistant.js.
streamProxy from @mariozechner/pi-agent-core.proxyUrl; the token is passed as authToken.createStreamFn() with the optional CORS proxy URL.scripts/esbuild.mjs bundles src/index.js to public/index.min.js.public/index.min.css.office-js is external.process is supplied by the esbuild inject/alias path src/shims/process.js.public/assets/.scripts/esbuild.mjs copies node_modules/pdfjs-dist/build/pdf.worker.min.mjs to public/pdfjs-dist/build/pdf.worker.min.mjs so pi-web-ui can load the PDF.js worker beside the bundle.marked, DOMPurify, Turndown, and turndown-plugin-gfm.src/<feature>/.src/task-pane/ is the composition root for assistant, settings, and pi-web-ui.src/index.js imports src/task-pane/index.js as the only feature entry, plus entry shims and styles.task-pane consumes assistant and settings only through their index.js barrels.assistant barrel exposes agent creation, createWordTools(), createPi4WordJavaScriptReplTool(), and registerCollapsibleWordToolRenderers().src/assistant-tools/index.js exposes tool factories for tests or custom tool sets.