From ai-ads-os
Use when generating Word documents (.docx). Uses the marble_docx API.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ai-ads-os:documents-docx-skillThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use the `marble_docx` helper library. It provides themed headings, tables, rules, cover pages, two-column layouts, and paragraphs — all with automatic theming.
Use the marble_docx helper library. It provides themed headings, tables, rules, cover pages, two-column layouts, and paragraphs — all with automatic theming.
IMPORTANT: Always use the library helpers below. Never manually define color constants, helper functions, or cell shading logic — the library handles all of this.
This skill is the complete API reference — all palettes, font pairs, and methods are documented below. Use them directly in your code without any exploration calls.
from marble_docx import Doc
doc = Doc(palette='ocean', fonts='modern')
# palette: midnight, forest, coral, terracotta, ocean, charcoal, teal, berry, sage, cherry
# fonts: classic, modern, bold, elegant, clean
# Or: Doc() for default light theme, Doc('dark') for dark theme
| Method | Purpose |
|---|---|
Doc(theme, font, palette, fonts) | Create document. palette for named colors, fonts for font pair |
doc.cover_page(title, subtitle, author, date) | Professional title page with accent lines |
doc.toc_placeholder() | Table of Contents heading placeholder |
doc.heading(text, level) | Heading with primary accent. Level 0=Title, 1=H1, 2=H2 |
doc.rule(color) | Horizontal divider line (default: primary). Use after section headings |
doc.paragraph(text, bold, italic, color, size, align) | Styled paragraph. Supports <b> and <i> |
doc.caption(text) | Secondary italic note |
doc.rich(*parts) | Mixed-style paragraph. Parts: strings or {'text', 'bold', 'color', ...} dicts |
doc.bullet(text) | Bullet list item. Supports <b> and <i> |
doc.numbered(text) | Numbered list item |
doc.table(headers, rows, col_widths, header_bg, header_fg) | Themed table with alternating rows |
doc.two_column_table(left_rows, right_rows) | Side-by-side content via invisible table |
doc.image(path, width, align) | Insert image |
doc.page_break() | Page break |
doc.header(text, align, color, bold) | Set page header |
doc.footer(text, align, color, bold) | Set page footer |
doc.raw | Access underlying docx.Document for advanced ops |
Choose a palette that matches the content's mood — not the default blue:
| Palette | Primary | Best for |
|---|---|---|
midnight | Deep navy | Finance, enterprise, formal |
forest | Rich green | Sustainability, health, growth |
coral | Warm pink-red | Marketing, creative, consumer |
terracotta | Earthy brown | Luxury, real estate, hospitality |
ocean | Deep blue | Tech, analytics, professional |
charcoal | Dark gray | Minimal, corporate, neutral |
teal | Vibrant teal | Modern, SaaS, healthcare |
berry | Deep plum | Premium, beauty, fashion |
sage | Muted green-blue | Consulting, calm, wellness |
cherry | Bold red | Bold reports, urgency, food |
| Name | Heading / Body |
|---|---|
classic | Georgia / Calibri |
modern | Calibri / Calibri Light |
bold | Arial Black / Arial |
elegant | Cambria / Calibri |
clean | Trebuchet MS / Calibri |
For reports and long documents, follow this structure:
doc.cover_page() + doc.page_break()doc.toc_placeholder() + doc.page_break() (optional)doc.rule() after every section headingThis gives visual structure and separates sections clearly:
doc.heading('1. Executive Summary', level=1)
doc.rule()
doc.paragraph('Key findings...')
col_widths on tablesWithout explicit widths, columns render unevenly:
doc.table(headers, rows, col_widths=[3.5, 1.5, 1.5]) # GOOD
doc.table(headers, rows) # BAD — uneven columns
doc.rich(
'Total spend was ',
{'text': '$793', 'bold': True},
' with a ROAS of ',
{'text': '0.70x', 'bold': True, 'color': 'error'},
)
doc.caption('Source: Meta Ads API | Data as of Feb 23, 2026')
{'text': '1.55x', 'color': 'success', 'bold': True} # green for positive
{'text': '0.38x', 'color': 'error', 'bold': True} # red for negative
Bold header variant: header_bg='primary', header_fg='text_on_primary'
When the document needs real images returned by previous tool calls (ad thumbnails from facebook_analyze_ad_creative_by_id_or_url, competitor ad images from the ad library, etc.), pass each image URL as an attachment_url data source on the SAME advanced_analysis call. The runtime downloads them into the workspace before your code runs — doc.image() takes the local path:
# Tool call payload:
# dataSources: [
# {type: 'attachment_url', identifier: 'https://cdn.fbcdn.net/.../thumb_a.jpg', alias: 'creative_1'},
# {type: 'attachment_url', identifier: 'https://cdn.fbcdn.net/.../thumb_b.png', alias: 'creative_2'},
# ]
# In code:
from marble_docx import Doc
doc = Doc(palette='ocean')
doc.heading('Top Performers', level=1)
doc.rule()
doc.image(f'{WORKSPACE}/creative_1.jpg', width=4)
doc.caption('Figure 1: Outdoor Adventure (Video) — CTR 2.46%')
doc.image(f'{WORKSPACE}/creative_2.png', width=4)
doc.caption('Figure 2: Forest Boots — CTR 1.92%')
doc.save(f'{WORKSPACE}/report.docx')
Do NOT import requests/urllib from inside your code — network is blocked and the call will fail with DNS errors. The attachment_url mechanism is the supported path. Extensions (.jpg/.png/.webp/.gif etc.) are auto-detected.
\n in text — always call doc.paragraph() or doc.bullet() separately for each line.col_widths — every table needs explicit column widths.doc.cover_page() for anything over 2 pages.doc.heading() without doc.rule() — headings need visual separation.'primary', 'success', 'error', 'secondary'.page_break() after every section — only use between major sections (e.g. after cover page, before appendix). Excessive page breaks create blank pages.doc.raw, always import what you need: from docx.enum.table import WD_TABLE_ALIGNMENT, from docx.enum.text import WD_ALIGN_PARAGRAPH, from docx.shared import Pt, Inches, RGBColor. The marble_docx library handles this automatically — prefer it over raw API.page_break() calls = fewer pages.from marble_docx import Doc
doc = Doc(palette='ocean', fonts='elegant')
doc.header('B&M Advertising | Performance Report', align='center', color='primary', bold=True)
doc.footer('Powered by GoMarble AI | Feb 2026')
# ── Cover page ──
doc.cover_page(
'B&M Advertising Account',
subtitle='Spend & ROAS Performance Report',
author='GoMarble AI',
date='February 17 – February 23, 2026',
)
doc.page_break()
# ── Executive Summary ──
doc.heading('1. Executive Summary', level=1)
doc.rule()
doc.paragraph('This report covers Meta Ads performance for B&M Advertising over the past 7 days.')
doc.rich(
'Total spend was ',
{'text': '$793.95', 'bold': True},
' with a blended ROAS of ',
{'text': '0.70x', 'bold': True, 'color': 'error'},
' — below the 1.0x break-even threshold.',
)
doc.caption('Note: Weighted ROAS is the spend-weighted average across all campaigns.')
# ── Key Metrics (two-column) ──
doc.heading('2. Key Metrics', level=1)
doc.rule()
doc.two_column_table(
['Total Spend: $793.95', 'Active Campaigns: 4'],
['Blended ROAS: 0.70x', 'Top ROAS: 2.83x (Test)'],
)
# ── Campaign Breakdown ──
doc.heading('3. Campaign Performance', level=1)
doc.rule()
doc.table(
headers=['Campaign', 'Spend', 'ROAS', 'Status'],
rows=[
['Prospecting', '$320', {'text': '0.55x', 'color': 'error', 'bold': True}, 'Active'],
['ASC', '$200', {'text': '0.38x', 'color': 'error', 'bold': True}, 'Active'],
['BOF', '$137', {'text': '1.55x', 'color': 'success', 'bold': True}, 'Active'],
['Test', '$136', {'text': '2.83x', 'color': 'success', 'bold': True}, 'Active'],
],
col_widths=[2.5, 1.2, 1.2, 1.2],
header_bg='primary', header_fg='text_on_primary',
)
# ── Chart ──
doc.image(f'{WORKSPACE}/roas_chart.png', width=6)
doc.caption('Figure 1: ROAS by Campaign')
# ── Recommendations ──
doc.heading('4. Recommendations', level=1)
doc.rule()
doc.bullet('<b>Pause Prospecting</b> — 0.55x ROAS, reallocate budget to BOF.')
doc.bullet('<b>Scale BOF</b> — 1.55x ROAS with $137 spend, room to grow.')
doc.bullet('<b>Expand Testing</b> — 2.83x ROAS is the highest performer.')
doc.bullet('<b>Review ASC targeting</b> — 0.38x ROAS suggests audience/creative issues.')
doc.save(f'{WORKSPACE}/report.docx')
result = {"title": "B&M Performance Report", "description": "Multi-page Word document with cover page and recommendations."}
For anything the library doesn't cover, use doc.raw:
doc = Doc()
raw = doc.raw # docx.Document object
# Helpers also exported:
from marble_docx import shade_cell, replace_image
shade_cell(cell, '1A73E8')
replace_image(doc, 0, f'{WORKSPACE}/new_chart.png', width=5.0)
For edits (fix text, update a table, remove a section), open the existing file — do NOT rebuild from scratch.
from docx import Document
doc = Document(f'{WORKSPACE}/report.docx')
for i, para in enumerate(doc.paragraphs):
if para.text.strip():
print(i, para.style.name, para.text[:80])
para = doc.paragraphs[5]
for run in para.runs:
run.text = run.text.replace('old value', 'new value')
para = doc.paragraphs[5]
para.clear()
run = para.add_run('New paragraph text')
run.font.size = Pt(11)
para = doc.paragraphs[5]
p = para._element
p.getparent().remove(p)
table = doc.tables[0] # first table
table.cell(1, 2).text = '$1,250' # row 1, col 2
table = doc.tables[0]
row = table.add_row()
row.cells[0].text = 'New Campaign'
row.cells[1].text = '$500'
from marble_docx import replace_image
doc = Document(f'{WORKSPACE}/report.docx')
# List images to find the right index
for i, shape in enumerate(doc.inline_shapes):
print(i, shape.width, shape.height)
# Replace image 0 with a new file, optionally resize
replace_image(doc, 0, f'{WORKSPACE}/new_chart.png', width=5.0)
doc.save(f'{WORKSPACE}/report.docx')
doc.save(f'{WORKSPACE}/report.docx')
result = {"title": "Updated Report", "description": "Fixed table values"}
Document(path)Always save to WORKSPACE:
doc.save(f'{WORKSPACE}/filename.docx')
npx claudepluginhub gomarble-ai/ai-ads-os --plugin ai-ads-osGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.