# Usage > Sync source: `src/plugin/tools/officecli.ts` (tool schemas — the single source of truth for this page), `README.md`, `docs/WORKFLOWS.md` The plugin registers two tools: `officecli` (single tool, action-based) and `edit` (overrides the built-in edit for draft routing). ## Core lifecycle ``` create / edit / revert → [draft] → accept → real file written ↖ undo (discard draft, release lock) ``` - `create` / `edit` never write the real file. They create or update a **draft** under the data dir and acquire the file lock (lazy acquire). - `accept` is the **single write path**: flushes the draft to disk, records an accept-point in history, releases the lock. - Binary files (`.png`, `.pdf`, `.docx`, `.xlsx`, `.pptx`, …): the `edit` override rejects them with "use officecli for binary files". Use `officecli` instead. ## Action reference All calls: `officecli(action="...", filePath="...")`. Array/object parameters are **JSON strings** (the tool schema takes strings and the code `JSON.parse`s them). | Action | Extra params | What it does | | --- | --- | --- | | `create` | `content` (required); `filePath` or `filePaths` | Create a new draft (no real file written). `filePaths`: JSON array → one draft per path. | | `edit` | `content` (required, non-empty) | Update the current session's draft; acquires lock if needed. | | `accept` | `filePath` or `filePaths`; `timestamp`? | Write draft → real file, record history, release lock. Batch mode is all-or-nothing. | | `undo` | — | Discard the draft, release the lock. | | `revert` | `timestamp` (accept-point from `history`) | Create a draft from that snapshot; still needs `accept` to write. | | `history` | — | Returns `[{timestamp, sessionID}]` accept-points for the file. | | `list` | `filePath`? | List active drafts across files (lock status, orphaned flag, age). | | `diff` | — | Unified markdown diff: draft vs real file. Review before `accept`. | | `generate` | `templatePath` + (`data` + `filePath` or `dataArray` + `filePaths`) | Substitute `{{var}}` placeholders in a Template; creates one draft per data entry. Missing keys → error listing them. `data`/`dataArray`/`filePaths` are JSON strings; `dataArray` and `filePaths` must be equal-length arrays. | | `preview` | — | Render draft (or real file) to HTML via pandoc; returns output path. | | `validate` | `rules` (JSON string, e.g. `{"required":[...],"patterns":[...]}`) | Run content rules against the draft; returns per-rule pass/fail report. | | `lock-status` | — | Lock details: sessionID, owner, status, stale, touchedAt. | | `force-release` | — | Take over a lock. Only allowed when the lock is **stale**; fresh foreign locks are rejected. | | `export` | `targetPath` (must differ from source) | Convert to a derived file (pdf/docx/xlsx/pptx) outside the write path. Text content is preserved; layout is approximate. | | `metadata` | `properties`? (JSON string) | With `properties`: write pending values to the draft sidecar. Without: read merged metadata (real file + sidecar). | | `watermark` | `text`; `position`?; `size`?; `opacity`? | DOCX/PDF watermark via sidecar. Empty `text` clears it. | | `annotate` | `annotations` (JSON string of overlay ops) | Image overlays: note / highlight / stamp (`DRAFT`, `APPROVED`, `CONFIDENTIAL`). `annotations: "[]"` clears. | | `comment` | `commentId`, `author`, `commentText`; anchor: range (DOCX) / `cellRef` (XLSX) / `slide`,`x`,`y` (PPTX); `suggestedText`? | Add a comment to the draft. With `suggestedText` it becomes an approvable suggestion. | | `approve` | `commentId` | Apply the suggestion's `suggestedText` into the draft and remove the comment. Fails if the comment has no `suggestedText`. | | `track-insert` / `track-delete` | `commentId`, `author`, `content`, `paragraph`, `offset` | DOCX-only native track changes. Errors on XLSX/PPTX — use `comment` with `suggestedText` there. | | `list-comments` | — | List comments in the draft or real file. | | `review` | — | Summary of all comments (+ track changes for DOCX). | ## `edit` override Registered as a plugin tool named `edit` (replaces the built-in). Signature: `{filePath, oldString, newString}`. - Text files: routed into the plugin draft lifecycle transparently — the agent just "edits". - Binary files: rejected, with the instruction to use `officecli`. ## Common workflows ### Simple create → review → accept ``` officecli(action="create", filePath="./report.docx", content="# Report...") officecli(action="diff", filePath="./report.docx") officecli(action="accept", filePath="./report.docx") ``` ### Read a document ``` officecli(action="read", filePath="./invoice.pdf") # returns markdown ``` ### Batch generation from a template Template = any file containing `{{var}}` placeholders (agent creates it with `create` + `accept` first): ``` officecli(action="generate", templatePath="./templates/decision-template.md", filePaths='["./decisions/a.docx", "./decisions/b.docx"]', dataArray='[{"DEPT": "Microbiology", "AMOUNT": 10000}, {"DEPT": "Radiology", "AMOUNT": 20000}]') officecli(action="accept", filePaths='["./decisions/a.docx", "./decisions/b.docx"]') ``` ### History and revert ``` officecli(action="history", filePath="./budget.docx") # → [{timestamp, sessionID}] officecli(action="revert", filePath="./budget.docx", timestamp=1234567890) officecli(action="accept", filePath="./budget.docx") ``` ### Locks (multi-session) - Locks are per-file claims; acquired lazily on the first mutating call by the session. - Another session's active lock → mutating calls fail with the owning session ID. - Check with `lock-status`; take over a stale lock with `force-release` (stale threshold is configurable, default 24h). - A session displaced by `force-release` gets an error on its next mutating call; its draft becomes an orphan. ### Format conversion ``` officecli(action="export", filePath="./report.docx", targetPath="./report.pdf") ``` Conversion goes through the markdown pipeline: content is preserved, exact layout is not. For layout-sensitive output, generate the target format directly instead. ## Errors - Real failures are thrown as typed `Tool.Error` — the tool call simply fails with the message. - Informational results (e.g. "no lock on X") are returned as normal output, not errors. - `generate` fails fast: bad JSON, mismatched array lengths, or missing template keys are all errors; nothing is silently left empty.