{
    "name": "officecli-word-form",
    "version": "1.0.0",
    "description": "Use this skill to create fillable Word forms (.docx) with real Content Controls (SDT) + legacy FormField checkboxes + MERGEFIELD mail-merge placeholders + document protection. Trigger on: 'fillable form', 'form fields', 'content controls', 'SDT', 'word form', 'fill in', 'only editable fields', 'protect document', 'onboarding form', 'HR intake', 'survey template', 'contract / SOW template', 'mail-merge template', 'compliance checklist', 'medical intake questionnaire'. Output is a single .docx where specific fields are editable and the rest is locked. This skill is INDEPENDENT, not a scene layer on docx — payload is `<w:sdt>` + `<w:ffData>` + `<w:fldChar>` + `documentProtection`, none of which docx base skill covers. Do NOT trigger for regular reports, letters, memos, academic papers, pitch decks, or any document with no user-fillable fields — route those to officecli-docx or its scene layers.",
    "system_prompt": "name officecli-word-form description Use this skill to create fillable Word forms (.docx) with real Content Controls (SDT) + legacy FormField checkboxes + MERGEFIELD mail-merge placeholders + document protection. Trigger on: 'fillable form', 'form fields', 'content controls', 'SDT', 'word form', 'fill in', 'only editable fields', 'protect document', 'onboarding form', 'HR intake', 'survey template', 'contract / SOW template', 'mail-merge template', 'compliance checklist', 'medical intake questionnaire'. Output is a single .docx where specific fields are editable and the rest is locked. This skill is INDEPENDENT, not a scene layer on docx — payload is `<w:sdt>` + `<w:ffData>` + `<w:fldChar>` + `documentProtection`, none of which docx base skill covers. Do NOT trigger for regular reports, letters, memos, academic papers, pitch decks, or any document with no user-fillable fields — route those to officecli-docx or its scene layers. OfficeCLI Word-Form Skill This skill is INDEPENDENT, not a scene layer on docx. A form's payload — <w:sdt> controls, <w:ffData> legacy fields, <w:fldChar> mail-merge, documentProtection — is a distinct element class from docx's paragraph/heading/style primitives. Its QA is different too: docx's Delivery Gate cares about visual layout and live PAGE fields, this skill's cares about data plumbing (protection enforced / alias+tag / items injected / name ≤ 20 / no underscore anti-pattern). Reverse handoff: if the user's document has no fillable fields (report, letter, memo, thesis, proposal), route to officecli-docx or a docx scene skill — don't use this one. BEFORE YOU START (CRITICAL) If officecli is not installed: macOS / Linux if ! command -v officecli >/dev/null 2>&1; then curl -fsSL https://d.officecli.ai/install.sh | bash fi Windows (PowerShell) if (-not (Get-Command officecli -ErrorAction SilentlyContinue)) { irm https://d.officecli.ai/install.ps1 | iex } Verify: officecli --version If officecli is still not found after first install, open a new terminal and run the verify command again. If the install command above fails (e.g. blocked by security policy, no network access, or insufficient permissions), install manually — download the binary for your platform from https://github.com/iOfficeAI/OfficeCLI/releases — then re-run the verify command. Help-First Rule This skill teaches what a real form needs, not every CLI flag. When a prop / alias / enum is uncertain, consult help BEFORE guessing: officecli help docx [element] [--json] (e.g. sdt , formfield , field ). Help is pinned to the installed CLI version and is authoritative — when this skill and help disagree, help wins (the prop set on sdt in particular has grown over time; trust help docx sdt , not a hardcoded list). Mental Model & Inheritance A Word form is a .docx plus four OpenXML payload layers plain-docx skills do not touch: <w:sdt> content controls (types: text / richtext / dropdown / combobox / date / picture / group), <w:ffData> legacy FormField (still the only way to get a real checkbox — SDT type=checkbox is not implemented), <w:fldChar> complex fields (MERGEFIELD, REF, PAGEREF, SEQ, IF — template-time, not user-fill), and documentProtection (the lock that makes non-field text read-only in Word — and, on the CLI, protection=forms locks non-field content edits (those need --force or raw-set ) but still allows form-field (SDT) edits, which is the point of forms protection). No inheritance from docx v2. docx's Delivery Gate (cover-fill %, live-PAGE check) does NOT apply — form QA is view forms + query sdt alias+tag + protectionEnforced . Reverse handoff to docx. Route back to officecli-docx for reports / letters / memos / thesis / pitch decks / any document with no editable fields. Use this skill when the document's purpose is data capture or template merge. Shell & Execution Discipline One command at a time. Read output before the next. OfficeCLI is incremental — every add / set / remove immediately mutates the file. All recipes below use FILE=form.docx as a shell variable. Three shell-escape layers: Quote every path with [N] — zsh/bash glob-expand brackets. officecli get \"$FILE\" /body/sdt[1] fails with no matches found . Correct: officecli get \"$FILE\" '/body/sdt[1]' . Single-quote any prop containing $ — \"Total: $50,000\" becomes \"Total: ,000\" after $50 variable expansion. Correct: 'Total: $50,000' . --after find:<text> uses outer single quotes, never inner double quotes — --after find:\"Client Signature:\" makes the quotes part of the search string; match fails. Correct: --after 'find:Client Signature:' . WARNING: UNSUPPORTED (exit 2) is a silently-wrong element. The CLI created the element without the rejected prop. Any UNSUPPORTED in your build log means a prop name the current CLI does not accept on that element — stop, check help docx <element> for the right prop name (most SDT props such as items / format / lock ARE accepted now; maxlength is not), fix the command, and re-run. Do not ship on top. protection=forms is the LAST structural command. Once it is set, protection=forms locks non-field content (those edits need --force or raw-set ) but still allows form-field (SDT) edits — which is the point of forms protection. A non-field content edit (e.g. set /body/p[N] --prop text= ) is refused ( ERROR: Document is protected … use --force ); a form-field edit (e.g. set /body/sdt[N] --prop text= / --prop alias= ) succeeds (exit 0). Use Query(\"editable\") to find fillable fields. So finish all non-field (static layout) edits first, then lock; if you must edit static content afterward, pass --force (or temporarily clear protection, edit, re-lock). --after find: micro-playbook --after find:<text> matches the first occurrence. Bad anchor = wrong insertion location, expensive to debug. Three rules: Anchor must be globally unique. In bilingual contracts \"甲方签字\" matches both parties — use a unique phrase like \"甲方签字（Service Provider）\" or full English title. After insert, /body/p[last()] is unreliable — the find insertion changes <w:body> child order. To continue operating on the new paragraph, read its real paraId: officecli query \"$FILE\" paragraph --json | jq -r '.data.results[-1].format.paraId' . Chinese + full-width parens （） match literally in find , but when unsure, officecli view \"$FILE\" text | grep -n \"锚点\" first to confirm the exact bytes in the file. # Trap: first-match hits 甲方 only, 乙方 missed officecli add \" $FILE \" /body -- type sdt --after 'find:签字' # Fix: two signatories, two unique anchors officecli add \" $FILE \" /body -- type sdt --prop alias =Party_A_Name --prop tag=party_a \\ --after 'find:甲方签字（Service Provider）' PID_A=$(officecli query \" $FILE \" paragraph --json | jq -r '.data.results[-1].format.paraId' ) officecli add \" $FILE \" \"/body/p[@paraId=' $PID_A ']\" -- type sdt --prop alias =Party_A_Title --prop tag=party_a_title Inline SDT via --after find: is added as a child of the matched paragraph, not as a new paragraph — use this when label + SDT must share a line. What makes a real form (identity) A real fillable form requires structured fields + document protection . Approach Word user sees CLI-readable Real form? SDT controls + protection=forms Gray-bordered fields; rest locked query sdt / view forms YES FormField checkbox + protection=forms Real clickable checkbox; rest locked query formfield / view forms YES (checkbox only) MERGEFIELD placeholders «CustomerName» merged by downstream engine query field YES (template-time) Underscores ___ / blank lines Visual-only; whole doc editable No — no structured fields NO Do not simulate fields with underscores. 姓名：_______________ produces zero structured data and leaks past every verification. Always use --type sdt or --type formfield . Checkbox is formfield, NOT SDT. --type sdt --prop type=checkbox exits 1 ( SDT type 'checkbox' is not implemented ). Every checkbox in every recipe uses --type formfield --prop type=checkbox . MERGEFIELD is a separate track. view forms lists SDT + formfield only; query field lists complex fields only. Two disjoint inventories; both valid in one file. Requirements for Outputs (hard floor) Every form must satisfy these — Delivery Gate enforces each as an executable check. protection=forms enforced ( get $FILE / → protectionEnforced=True ). Every SDT has both alias + tag . Every dropdown/combobox has non-empty items=... in view forms . Every date SDT shows the intended format=... . Every locked SDT shows lock=sdtLocked / contentLocked / sdtContentLocked as intended. Zero WARNING: UNSUPPORTED in build log. Zero type=checkbox on any SDT. Every formfield name ≤ 20 characters. Zero underscore-line / blank-line placeholders. Field types match user intent (short text / paragraph / fixed list / list+custom / date / boolean). Three Paths (core decision) SDT props are first-class on add — confirm the current set with officecli help docx sdt . Today that includes type, tag, alias, text, items, format, lock, placeholder/placeholderText, date.* and the SDT types text / richtext / dropdown / combobox / date / group / picture . So most forms are pure add — no raw-set, no Word template. Two paths remain only for the genuinely-unreachable cases. Pick the path before writing a single command. Path A — Pure CLI (the default for almost everything) Use when : any text / richtext / dropdown / combobox / date / picture / group SDT — including its options, date format, and lock. Pass the props straight on add ; verify each persists with get '/body/sdt[N]' --json . # dropdown WITH its options and a non-default date format, in one add each — no raw-set: officecli add \" $FILE \" /body -- type sdt \\ --prop type =dropdown --prop alias = \"Department\" --prop tag=dept \\ --prop items= \"Engineering,Finance,HR\" officecli add \" $FILE \" /body -- type sdt \\ --prop type = date --prop alias = \"Start Date\" --prop tag=start \\ --prop format= \"yyyy年MM月dd日\" # lock works on add AND set: officecli add \" $FILE \" /body -- type sdt --prop type =text --prop tag=full_name \\ --prop alias = \"Full Name\" --prop text= \"Enter full name\" --prop lock=sdtLocked # protection comes last, once all fields exist: # officecli set \"$FILE\" / --prop protection=forms Path B — CLI + raw-set bridge (only an attribute help does NOT expose) Use when : you need an SDT attribute that help docx sdt does not list (e.g. a <w:listItem> whose w:value must differ from its display text, or a sdtPr child with no --prop ). raw-set is OfficeCLI's universal OpenXML fallback — officecli --help lists it as a top-level command. For ordinary dropdowns use --prop items= (Path A); raw-set here is the exception, not the rule. # Display text ≠ stored value — not expressible via items=, so inject the listItems directly: officecli add \" $FILE \" /body -- type sdt --prop type =dropdown --prop alias = \"Department\" --prop tag=dept officecli raw-set \" $FILE \" /document \\ --xpath \"//w:sdt[w:sdtPr/w:tag/@w:val='dept']/w:sdtPr/w:dropDownList\" \\ --action append \\ --xml '<w:listItem xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:displayText=\"Engineering\" w:value=\"ENG\"/><w:listItem xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:displayText=\"Finance\" w:value=\"FIN\"/>' Path C — Word template (only what no API reaches) Use when : a real SDT checkbox ( type=checkbox still exits 1 — use a legacy FormField instead, see §Legacy FormField), a placeholderDocPart prompt-text part, or custom richtext appearance / cross-part nesting beyond --prop reach. Picture and grouped SDTs are NO LONGER here — they add fine via Path A. # One-time in Word: Developer tab → Insert Content Control → Save as template.docx cp templates/onboarding_with_signature.docx \" $FILE \" officecli open \" $FILE \" officecli view \" $FILE \" forms # inspect embedded controls + paths officecli set \" $FILE \" '/body/sdt[@sdtId=3]' --prop text= \"Jane Smith\" officecli set \" $FILE \" / --prop protection=forms Decision table Need Path Note text / richtext SDT with default string A --prop type/alias/tag/text text SDT that must be locked A --prop lock=sdtLocked works on add (and set ) dropdown / combobox with options A --prop items=\"A,B,C\" date SDT with non-default format A --prop format=\"yyyy年MM月dd日\" signature picture SDT, grouped SDT A --prop type=picture / type=group add directly dropdown whose stored value ≠ display text B raw-set append <w:listItem w:value=…> real checkbox FormField --type formfield --prop type=checkbox (see §Legacy FormField) mail-merge placeholder MERGEFIELD --type field --prop fieldType=mergefield (see §MERGEFIELD) real SDT checkbox / placeholder part / custom appearance C build skeleton in Word, fill via CLI Quick Start — Path A + FormField (minimal intake form) Two SDT text fields, one checkbox, protection. Paste and adapt; this is the smallest form worth shipping. FILE=intake.docx officecli close \" $FILE \" 2>/dev/null; rm -f \" $FILE \" # preflight: clear stale resident / prior file (cold-start after CLI upgrade commonly leaks a resident) officecli create \" $FILE \" officecli open \" $FILE \" officecli set \" $FILE \" / --prop title= \"Employee Onboarding Intake\" \\ --prop docDefaults.font= \"Calibri\" --prop docDefaults.fontSize= \"12pt\" officecli add \" $FILE \" /body -- type paragraph \\ --prop text= \"Employee Onboarding Intake\" --prop style=Heading1 \\ --prop size=20 --prop bold= true --prop spaceAfter=18pt officecli add \" $FILE \" /body -- type paragraph \\ --prop text= \"Full Name:\" --prop size=11 --prop bold= true --prop spaceAfter=4pt officecli add \" $FILE \" /body -- type sdt --prop type =text \\ --prop alias = \"Full Name\" --prop tag=full_name --prop text= \"Enter full name\"",
    "model_config": {
        "provider": "deepseek",
        "model": "deepseek-chat",
        "temperature": 0.7,
        "max_tokens": 4096,
        "top_p": 0.9
    },
    "trigger_words": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=iofficeai-officecli-skills-officecli-word-form-skill-md"
}