
Automating Word
- 16 installs
- 39 repo stars
- Updated January 14, 2026
- spillwavesolutions/automating-mac-apps-plugin
Helps with ai & agent building tasks during AI-assisted development.
About
automating-word is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- automating-word
- AI & Agent Building
- AI-coding skill
Automating Word by the numbers
- 16 all-time installs (skills.sh)
- Ranked #11,040 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/spillwavesolutions/automating-mac-apps-plugin --skill automating-wordAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 16 |
|---|---|
| repo stars | ★ 39 |
| Last updated | January 14, 2026 |
| Repository | spillwavesolutions/automating-mac-apps-plugin ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Automating Word (JXA-first, AppleScript discovery)
Relationship to the macOS automation skill
- Standalone for Word, aligned with
automating-mac-appspatterns. - Use
automating-mac-appsskill for permissions, shell execution, and UI scripting guidance. - PyXA Installation: To use PyXA examples in this skill, see the installation instructions in
automating-mac-appsskill (PyXA Installation section).
Core framing
- Word dictionary is AppleScript-first; discover there.
- JXA provides logic, data handling, and ObjC bridge access.
- Objects are specifiers; read via methods, write via assignments.
- Handle errors from Word operations using try/catch blocks and Application error checking.
Implementation Workflow
1. Discover AppleScript Dictionary: Open Script Editor, browse Word's AppleScript dictionary to understand available objects and methods. 2. Translate to JXA: Use discovered AppleScript syntax as reference for JXA equivalents, consulting the dictionary translation table. 3. Set Up JXA Script: Initialize Word application object and document references. 4. Implement Operations: Apply find/replace, table manipulation, or export using JXA methods. 5. Test and Validate: Run script and verify document changes match expectations.
Quick Examples
Document opening:
// JXA
const word = Application('Microsoft Word');
word.documents.open('/path/to/document.docx');# PyXA (Recommended)
import PyXA
word = PyXA.Word()
word.documents().open("/path/to/document.docx")Find and replace:
// JXA
const range = word.activeDocument.content;
range.find.text = 'old text';
range.find.replacement.text = 'new text';
range.find.execute({replace: 'all'});# PyXA
doc = word.active_document()
find_obj = doc.content().find()
find_obj.text = 'old text'
find_obj.replacement.text = 'new text'
find_obj.execute(replace='all')Table creation:
// JXA
const table = word.activeDocument.tables.add(word.activeDocument.content, 3, 4);
table.cell(1, 1).range.text = 'Header';# PyXA
table = doc.tables().add(doc.content(), 3, 4)
table.cell(1, 1).range().text = 'Header'For PyObjC Scripting Bridge examples, see automating-word/references/word-pyxa.md.
Validation Checklist
After implementing Word automation:
- [ ] Test script execution without errors
- [ ] Verify document changes applied correctly
- [ ] Check ObjC bridge objects return expected values
- [ ] Run find/replace operations and confirm replacements
- [ ] Export documents and validate output formats
When Not to Use
- For general macOS automation (use
automating-mac-apps) - For Excel automation (use
automating-excel) - For non-Microsoft Office applications
- For web-based document processing (use web APIs or Playwright)
What to load
- Word JXA basics:
automating-word/references/word-basics.md(core concepts only; see references for advanced usage) - Recipes (ranges, find/replace, tables):
automating-word/references/word-recipes.md - Advanced patterns (export enums, ObjC bridge):
automating-word/references/word-advanced.md - Dictionary translation table:
automating-word/references/word-dictionary.md - PyXA (Python) alternative:
automating-word/references/word-pyxa.md
Word JXA Advanced Patterns
The Objective-C Bridge
JXA's ObjC bridge provides access to macOS frameworks, enabling functionality beyond Word's AppleScript dictionary.
Import Foundation Framework
ObjC.import('Foundation');File System Management with NSFileManager
Check File Existence
function fileExists(posixPath) {
const fileManager = $.NSFileManager.defaultManager;
const nsPath = $(posixPath).stringByStandardizingPath;
return fileManager.fileExistsAtPath(nsPath);
}
// Usage - validate before opening
if (fileExists("/Users/you/Documents/Report.docx")) {
const doc = word.open("/Users/you/Documents/Report.docx");
}Read Text Files with Proper Encoding
AppleScript's read command struggles with UTF-8. Use NSString:
function readFileContent(path) {
const errorRef = $();
const nsPath = $(path).stringByStandardizingPath;
const nsString = $.NSString.stringWithContentsOfFileEncodingError(
nsPath,
$.NSUTF8StringEncoding,
errorRef
);
if (errorRef.code) {
const errorDesc = ObjC.unwrap(errorRef.localizedDescription);
throw new Error(`Failed to read: ${path}. ${errorDesc}`);
}
return ObjC.unwrap(nsString);
}Write Text Files
function writeFileContent(path, content) {
const nsString = $(content);
const nsPath = $(path).stringByStandardizingPath;
const errorRef = $();
nsString.writeToFileAtomicallyEncodingError(
nsPath,
true,
$.NSUTF8StringEncoding,
errorRef
);
if (errorRef.code) {
throw new Error(`Write failed: ${ObjC.unwrap(errorRef.localizedDescription)}`);
}
}Clipboard Bridge for Images (Sandbox Workaround)
Word's inlineShapes.addPicture() often fails due to macOS sandboxing. Bypass via clipboard:
ObjC.import('AppKit');
function loadImageToClipboard(imagePath) {
const pasteboard = $.NSPasteboard.generalPasteboard;
pasteboard.clearContents;
const nsImage = $.NSImage.alloc.initWithContentsOfFile(imagePath);
if (!nsImage || !nsImage.valid) {
return false;
}
const array = $.NSArray.arrayWithObject(nsImage);
pasteboard.writeObjects(array);
return true;
}
// Usage: Load to clipboard, then paste in Word
function insertImageViaPaste(doc, imagePath) {
if (loadImageToClipboard(imagePath)) {
// Position cursor where image should go
const range = doc.content;
range.collapse({ direction: 0 });
// Paste from clipboard
range.paste();
}
}Batch Processing Pattern
Process multiple documents efficiently:
function batchConvertToPDF(inputDir, outputDir) {
ObjC.import('Foundation');
const word = Application("Microsoft Word");
word.includeStandardAdditions = true;
const fm = $.NSFileManager.defaultManager;
const contents = fm.contentsOfDirectoryAtPathError(inputDir, $());
// Filter for .docx files
const files = ObjC.unwrap(contents).filter(f => f.endsWith('.docx'));
try {
word.screenUpdating = false;
word.displayAlerts = false;
for (const file of files) {
try {
const inputPath = `${inputDir}/${file}`;
const outputPath = `${outputDir}/${file.replace('.docx', '.pdf')}`;
const doc = word.open(inputPath);
doc.saveAs({
fileName: outputPath,
fileFormat: 17 // PDF
});
doc.close({ saving: 'no' });
console.log(`Converted: ${file}`);
} catch (e) {
console.log(`Error processing ${file}: ${e.message}`);
// Continue with next file
}
}
} finally {
word.screenUpdating = true;
word.displayAlerts = true;
}
}Debugging and Error Handling
Try/Catch Pattern
try {
word.open(path);
} catch (e) {
console.log("Word Error: " + e.message);
// e.message often contains Apple Event error number
}Common Error Codes
| Code | Meaning |
|---|---|
| -1700 | Type conversion failed |
| -1728 | Object not found |
| -10000 | Application not running |
| -1708 | Event not handled |
Logging with NSLog
For real-time debugging when console.log is buffered:
function log(msg) {
$.NSLog($.NSString.alloc.initWithUTF8String(msg));
}Calling VBA Macros from JXA
When JXA lacks functionality, call existing VBA:
// Execute a VBA macro stored in the workbook
word.run("FormatReport");
// Execute with parameters
word.run("UpdateCalculation", { arg1: 500 });AppleScript Dictionary Translation Rules
When reading AppleScript documentation:
1. Elements become Collections: worksheet -> workbook.worksheets 2. Properties become CamelCase: display alerts -> displayAlerts 3. Commands use parameter objects: save in folder -> save({ in: folder })
Mapping Table
| AppleScript | JXA |
|---|---|
set x to value of range "A1" | var x = range.value() |
set value of range "A1" to x | range.value = x |
count of worksheets | wb.worksheets.length |
make new document | word.make({ new: 'document' }) |
delete worksheet "Sheet1" | ws.delete() |
Production Script Template
/**
* Word Automation Controller
* Production-grade template with proper error handling
*/
'use strict';
ObjC.import('Foundation');
function run() {
const word = Application("Microsoft Word");
word.includeStandardAdditions = true;
// Store original state
const originalScreenUpdating = word.screenUpdating;
try {
word.screenUpdating = false;
word.displayAlerts = false;
// === YOUR AUTOMATION LOGIC HERE ===
console.log("Automation completed successfully");
} catch (error) {
console.log("Critical Error: " + error.message);
// Re-enable alerts to show error
word.displayAlerts = true;
} finally {
// ALWAYS restore state
word.screenUpdating = true;
word.displayAlerts = true;
}
}Word JXA Basics
Runtime Architecture
JXA scripts run in a separate process from Word. Communication occurs via Apple Events (IPC). When you access wordApp.documents, JXA creates an Object Specifier (a query reference), not the actual data. Data is only fetched when properties are explicitly accessed.
Bootstrapping
'use strict';
// Initialize the proxy for Microsoft Word
const word = Application("Microsoft Word");
word.includeStandardAdditions = true;
// Bring to front
word.activate();
// Access the name property triggers an Apple Event
const appName = word.name();Global Settings for Performance
Before intensive operations, disable UI updates:
try {
word.screenUpdating = false; // Freeze UI during batch operations
word.displayAlerts = false; // Suppress confirmation dialogs
// ... perform automation tasks ...
} finally {
// ALWAYS restore settings
word.screenUpdating = true;
word.displayAlerts = true;
}Document Lifecycle
Creating Documents
// Create new document using make command
const newDoc = word.make({
new: 'document',
withProperties: {
// Initial properties can be set here
}
});Opening Documents
// Open existing document - use absolute POSIX path
const doc = word.open("/Users/you/Documents/Report.docx");Saving Documents
// Save to existing location
doc.save();
// Save As with specific format
doc.saveAs({
fileName: "/Users/you/Desktop/NewReport.docx",
fileFormat: 16 // wdFormatDocx
});Closing Documents
doc.close({
saving: 'no' // or 'yes' to save changes
});File Format Enumerations
| Constant | Value | Description |
|---|---|---|
| wdFormatDocument | 16 | Default .docx |
| wdFormatPDF | 17 | PDF export |
| wdFormatText | 2 | Plain text |
| wdFormatRTF | 6 | Rich Text Format |
| wdFormatHTML | 8 | HTML format |
| wdFormatFlatXML | 19 | Flat XML |
Getting Document Content
// Get full document text
const fullText = doc.content.content();
// Access specific paragraph
const firstPara = doc.paragraphs[0].textObject.content();
// Count paragraphs
const paraCount = doc.paragraphs.length;The Object Specifier Model
// This does NOT fetch data - creates a reference
const sheet = workbook.sheets[0];
// This DOES fetch data - triggers Apple Event
const sheetName = sheet.name();
// To inspect object properties
const props = doc.properties();
console.log(JSON.stringify(props));Word dictionary translation table
AppleScript JXA
----------------------------------- ------------------------------------------
front document word.documents[0]
content of document doc.content
text of content doc.content.content()
make new document word.make({ new: 'document' })
find text doc.content.find
save as PDF doc.saveAs({ fileName: "...", fileFormat: 17 })Notes:
- Use integer enums for file formats.
- Prefer Range over Selection.
Word Automation with PyXA (Python)
PyXA is a Python library for macOS automation that wraps Apple's Scripting Bridge. It provides a Pythonic interface to control Microsoft Word.
Installation
pip install pyxaBootstrapping
import PyXA
# Launch or activate Word
word = PyXA.Application("Microsoft Word")
word.activate() # Launches if not runningDocument Lifecycle
Creating Documents
# Create new document
doc = word.documents.new()Opening Documents
# Open existing document
doc = word.open("/Users/me/Documents/Report.docx")Saving Documents
# Save to new location
doc.save_as("/Users/me/Desktop/Output.docx")
# Save existing
doc.save()Content Manipulation
Inserting Text
# Insert text at end
doc.text_objects.insert("Hello, PyXA!", at=doc.text_objects.end())
# Insert at specific position
doc.content.insert("Introduction\n", at=0)Working with Paragraphs
# Access paragraphs
first_para = doc.paragraphs[0]
all_paras = doc.paragraphs
# Get paragraph text
text = first_para.textFormatting
Paragraph Formatting
# Bold, font size, alignment
doc.paragraphs[0].bold = True
doc.paragraphs[0].font_size = 14
doc.paragraphs[0].alignment = PyXA.WD_ParagraphAlignment.center
# Bulk formatting
doc.paragraphs[0:5].bold = TrueCharacter Formatting
# Font properties
doc.characters.font_name = "Arial"
doc.characters.font_size = 12
doc.characters.font_color = PyXA.XAColor.blueTables
# Add table at end of document
table = doc.tables.add(
range_object=doc.range(start=doc.characters.end() - 1),
rows=3,
columns=4
)
# Populate cells
table.cells.item(1, 1).text = "Header 1"
table.cells.item(1, 2).text = "Header 2"
table.cells.item(2, 1).text = "Data A"
table.cells.item(2, 2).text = "Data B"Find and Replace
# Simple find/replace
doc.content.find("old text").replace("new text")
# Replace all occurrences
doc.content.replace_all("foo", "bar")Export to PDF
# Export as PDF
doc.save_as("/Users/me/Desktop/Report.pdf",
file_format=PyXA.WD_FileFormat.pdf)Complete Example
import PyXA
def create_report(output_path, title, sections):
"""Generate a formatted Word document with PyXA."""
word = PyXA.Application("Microsoft Word")
word.activate()
# Create document
doc = word.documents.new()
# Add title
doc.content.insert(f"{title}\n\n")
doc.paragraphs[0].bold = True
doc.paragraphs[0].font_size = 24
doc.paragraphs[0].alignment = PyXA.WD_ParagraphAlignment.center
# Add sections
for section_title, content in sections:
doc.content.insert(f"\n{section_title}\n", at=doc.content.end())
doc.content.insert(f"{content}\n", at=doc.content.end())
# Save as PDF
doc.save_as(output_path, file_format=PyXA.WD_FileFormat.pdf)
doc.close()
return {"success": True, "path": output_path}
# Usage
sections = [
("Overview", "This report covers Q4 performance."),
("Results", "Revenue increased by 25%."),
("Conclusion", "Strong quarter overall.")
]
create_report("/Users/me/Desktop/Q4Report.pdf", "Q4 Report", sections)PyXA vs JXA Comparison
| Feature | PyXA (Python) | JXA (JavaScript) |
|---|---|---|
| Syntax | Pythonic | JavaScript |
| Installation | pip install pyxa | Built-in |
| Data Structures | Python lists/dicts | JS arrays/objects |
| Error Handling | try/except | try/catch |
| Performance | Similar (both use Scripting Bridge) | Similar |
| IDE Support | Full Python tooling | Script Editor only |
Notes
- PyXA wraps Apple's Scripting Bridge (same foundation as JXA)
- Install via
pip install pyxa(requires macOS) - Apps must support AppleScript/Scripting Bridge
- Property assignment is direct:
obj.property = value - Use enums for format constants (e.g.,
PyXA.WD_FileFormat.pdf)
Word JXA Recipes
Text Manipulation with Range Objects
The Range object allows programmatic text manipulation without changing cursor position.
Append Text via Range
const range = doc.content;
range.collapse({ direction: 0 }); // 0 = collapse to end
range.insertAfter("Automated Appendix\n");Collapse Direction Enums
| Constant | Value | Description |
|---|---|---|
| wdCollapseStart | 1 | Collapse to beginning |
| wdCollapseEnd | 0 | Collapse to end |
Insert Text at Specific Position
// Insert after specific paragraph
const para = doc.paragraphs[2];
const range = para.textObject;
range.collapse({ direction: 0 });
range.insertAfter("\n[INSERTED SECTION]\n");Find and Replace
Global Replace All
function performGlobalReplace(doc, searchString, replaceString) {
const range = doc.content;
const findObj = range.find;
// Clear previous settings
findObj.clearFormatting();
findObj.replacement.clearFormatting();
// Set search criteria
findObj.text = searchString;
findObj.replacement.text = replaceString;
findObj.forward = true;
findObj.matchCase = false;
findObj.matchWholeWord = true;
findObj.matchWildcards = false;
// Execute replace all
findObj.execute({
replace: 2, // wdReplaceAll
wrap: 1 // wdFindContinue
});
}
// Usage: Template filling
performGlobalReplace(doc, "{{CLIENT}}", "Acme Corporation");
performGlobalReplace(doc, "{{DATE}}", new Date().toLocaleDateString());Find/Replace Enums
| Constant | Value | Description |
|---|---|---|
| wdReplaceNone | 0 | Search only |
| wdReplaceOne | 1 | Replace first |
| wdReplaceAll | 2 | Replace all |
| wdFindStop | 0 | Stop at end |
| wdFindContinue | 1 | Wrap around |
| wdFindAsk | 2 | Ask user |
Tables
Create and Populate Table
function insertReportTable(doc, rowCount, colCount) {
const range = doc.content;
range.collapse({ direction: 0 }); // Append to end
const table = doc.tables.add(range, rowCount, colCount);
table.style = "Table Grid";
return table;
}
// Populate cells
const table = insertReportTable(doc, 5, 4);
table.rows[0].cells[0].content = "Header 1";
table.rows[0].cells[1].content = "Header 2";
table.rows[1].cells[0].content = "Data 1";Fast Table Population (Tab-Delimited)
For large tables, avoid cell-by-cell Apple Events:
function fastTableGeneration(doc, dataArray) {
const range = doc.content;
range.collapse({ direction: 0 });
// Convert 2D array to tab-delimited string
const flatText = dataArray.map(row => row.join('\t')).join('\r');
range.insertAfter(flatText);
// Then convert to table (much faster than cell-by-cell)
// Note: May need to select and convert manually
}Formatting
Direct Font Formatting
const range = doc.paragraphs[0].textObject;
range.font.name = "Helvetica Neue";
range.font.size = 14;
range.font.bold = true;
range.font.colorIndex = 6; // wdRedApply Named Styles
// Apply heading style
doc.paragraphs[0].style = "Heading 1";
// Apply body style
doc.paragraphs[1].style = "Normal";Warning: Style names are localized. "Heading 1" may fail on non-English systems.
PDF Export
const WdSaveFormat = { PDF: 17, DOCX: 16 };
function saveAsPDF(doc, outputPath) {
doc.saveAs({
fileName: outputPath,
fileFormat: WdSaveFormat.PDF
});
}
// Usage
saveAsPDF(doc, "/Users/you/Desktop/Report.pdf");Template Filler Pattern
Complete workflow for document generation:
function fillTemplate(templatePath, outputPath, replacements) {
const word = Application("Microsoft Word");
word.includeStandardAdditions = true;
try {
word.screenUpdating = false;
word.displayAlerts = false;
// Open template
const doc = word.open(templatePath);
// Apply all replacements
for (const [placeholder, value] of Object.entries(replacements)) {
performGlobalReplace(doc, placeholder, value);
}
// Save as PDF
doc.saveAs({
fileName: outputPath,
fileFormat: 17 // PDF
});
doc.close({ saving: 'no' });
} finally {
word.screenUpdating = true;
word.displayAlerts = true;
}
}
// Usage
fillTemplate(
"/Users/you/Templates/Invoice.docx",
"/Users/you/Desktop/Invoice_001.pdf",
{
"{{CLIENT}}": "Acme Corp",
"{{AMOUNT}}": "$5,000",
"{{DATE}}": "January 14, 2025"
}
);