Agentman DOCX Style Guide (JavaScript / docx-js)

When to Use

Load this skill before writing any DOCX generation code using docx-js. This skill provides the complete Agentman brand implementation for Word document output.

The brand identity is quiet confidence — charcoal suits, not fire trucks. Carbon and warm neutrals do the heavy lifting. Terracotta appears as a rare, deliberate accent.

STOP — Read Before Writing Any Code

Before writing a SINGLE line of DOCX generation code, you MUST:

  1. Define ALL color constants below as the first lines of your script
  2. Set document metadata — creator, title, company
  3. Display author on page 1 immediately after the report title
  4. NEVER let Word default styles bleed through — override every heading, body text, and table style
  5. NEVER use default heading colors — Word defaults are blue/black, not Agentman charcoal
  6. NEVER use ShadingType.SOLID — always ShadingType.CLEAR to prevent black backgrounds
  7. Always use WidthType.DXA — never WidthType.PERCENTAGE (breaks in Google Docs)
  8. Inter for headings and document title. Calibri for everything else.

Font Strategy

Two fonts. One rule: Inter for headings, Calibri for everything else.

Font Elements Why
Inter Document title, H1, H2, H3 Geometric, modern — stronger visual anchor; matches brand's editorial posture
Calibri Body text, tables, footer, captions, stat blocks Universally available, renders consistently without embedding

Inter must be installed on the generating machine or embedded in the output. Download from https://fonts.google.com/specimen/Inter (free, OFL license).

Never substitute Inter headings with Calibri, Arial, or Helvetica. If Inter is unavailable, log a warning and fall back to Calibri — do not silently use a different font.

Visual Weight Distribution

  • 70% Carbon/Charcoal (141413, 292322, 3D3735) — the backbone
  • 20% Warm Neutrals (F0EEE6, F6EAE6, E3DACC, FAF9F5, FFFFFF) — warmth without color
  • 10% Terracotta Accent (CC785C, D97757, A65945) — sparingly, max 3 per section

Color Constants

const fs = require("fs");
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
        Header, Footer, AlignmentType, HeadingLevel, BorderStyle, WidthType,
        ShadingType, VerticalAlign, PageNumber, PageBreak } = require("docx");

// Font constants
const FONT_HEADING = "Inter";     // H1, H2, H3, document title
const FONT_BODY    = "Calibri";   // All other text

// Primary Brand (Terracotta) — use sparingly, max 3 per section
const AGENTMAN_800 = "703B2D";   // Badge text
const AGENTMAN_700 = "8B4A38";   // Cover accent
const AGENTMAN_600 = "A65945";   // Negative emphasis
const AGENTMAN_500 = "CC785C";   // PRIMARY ACCENT — stat numbers, table header text
const AGENTMAN_400 = "D97757";   // Highlight/caution
const AGENTMAN_200 = "E6A890";   // Badge borders
const AGENTMAN_150 = "E3DACC";   // Table borders, separators
const AGENTMAN_100 = "F6EAE6";   // Table header fill
const AGENTMAN_75  = "F0EEE6";   // Highlight row bg, callout blocks
const AGENTMAN_50  = "FAF9F5";   // Alternate row bg (cream)

// Text Colors (Charcoal) — 70% of visual weight
const CHARCOAL_950 = "141413";   // PRIMARY BODY TEXT
const CHARCOAL_900 = "292322";   // Section titles, cover title
const CHARCOAL_800 = "3D3735";   // Secondary text

// Neutral (Slate)
const SLATE_600    = "475569";   // Metric labels
const SLATE_500    = "64748B";   // Footer, muted captions

Standard Borders

const BRAND_BORDER = { style: BorderStyle.SINGLE, size: 1, color: AGENTMAN_150 };
const BRAND_BORDERS = { top: BRAND_BORDER, bottom: BRAND_BORDER, left: BRAND_BORDER, right: BRAND_BORDER };
const NO_BORDER = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const NO_BORDERS = { top: NO_BORDER, bottom: NO_BORDER, left: NO_BORDER, right: NO_BORDER };
const TERRACOTTA_BOTTOM_BORDER = { bottom: { style: BorderStyle.SINGLE, size: 6, color: AGENTMAN_500, space: 1 } };

Document Setup

const PAGE_WIDTH = 12240;  // 8.5in DXA (CRITICAL: docx-js defaults to A4)
const PAGE_HEIGHT = 15840; // 11in DXA
const PAGE_MARGIN = 1080;  // 0.75in
const CONTENT_WIDTH = PAGE_WIDTH - (PAGE_MARGIN * 2); // 10080

function createAgentmanDoc(reportTitle, sections) {
  return new Document({
    creator: "Agentman Equity Research Assistant",
    title: `Agentman — ${reportTitle}`,
    company: "Agentman (Chain of Agents, Inc.)",
    styles: {
      default: { document: { run: { font: FONT_BODY, size: 22, color: CHARCOAL_950 } } },
      paragraphStyles: [
        { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true,
          run: { size: 48, bold: true, font: FONT_HEADING, color: CHARCOAL_900 },
          paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 } },
        { id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true,
          run: { size: 36, bold: true, font: FONT_HEADING, color: CHARCOAL_900 },
          paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 } },
        { id: "Heading3", name: "Heading 3", basedOn: "Normal", next: "Normal", quickFormat: true,
          run: { size: 28, bold: true, font: FONT_HEADING, color: CHARCOAL_900 },
          paragraph: { spacing: { before: 120, after: 120 }, outlineLevel: 2 } },
      ],
    },
    sections,
  });
}

const SECTION_PROPS = {
  page: { size: { width: PAGE_WIDTH, height: PAGE_HEIGHT },
          margin: { top: PAGE_MARGIN, bottom: PAGE_MARGIN, left: PAGE_MARGIN, right: PAGE_MARGIN } },
};

Cover Page

function buildCoverPage(reportTitle, dateStr, subtitle) {
  const today = dateStr || new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
  const children = [
    new Paragraph({ heading: HeadingLevel.HEADING_1, children: [
      new TextRun({ text: `Agentman — ${reportTitle}`, font: FONT_HEADING, bold: true, size: 48, color: CHARCOAL_900 })
    ]}),
  ];
  if (subtitle) children.push(new Paragraph({ spacing: { before: 40, after: 120 }, children: [
    new TextRun({ text: subtitle, font: FONT_BODY, size: 26, color: SLATE_600 })
  ]}));
  children.push(
    new Paragraph({ spacing: { after: 60 }, children: [
      new TextRun({ text: "Author: Agentman Equity Research Assistant", font: FONT_BODY, size: 22, color: SLATE_600 })
    ]}),
    new Paragraph({ spacing: { after: 200 }, children: [
      new TextRun({ text: `${today} | Data Source: Financial Modeling Prep`, font: FONT_BODY, size: 22, color: SLATE_600 })
    ]}),
    terracottaRule()
  );
  return children;
}

Helper Functions

function terracottaRule(thickness = 18) {
  return new Paragraph({ spacing: { before: 60, after: 120 }, border: { bottom: { style: BorderStyle.SINGLE, size: thickness, color: AGENTMAN_500, space: 1 } }, children: [] });
}

function sectionHeading(text, level = HeadingLevel.HEADING_2) {
  return new Paragraph({ heading: level, border: TERRACOTTA_BOTTOM_BORDER, spacing: { before: 240, after: 120 }, children: [
    new TextRun({ text, font: FONT_HEADING, bold: true, size: level === HeadingLevel.HEADING_2 ? 36 : 28, color: CHARCOAL_900 })
  ]});
}

function bodyText(text) { return new Paragraph({ spacing: { after: 120 }, children: [new TextRun({ text, font: FONT_BODY, size: 22, color: CHARCOAL_950 })] }); }
function secondaryText(text) { return new Paragraph({ spacing: { after: 80 }, children: [new TextRun({ text, font: FONT_BODY, size: 20, color: SLATE_600 })] }); }

Table Styling

function agentmanTable(headers, data, colWidths) {
  const numCols = headers.length;
  if (!colWidths) { const w = Math.floor(CONTENT_WIDTH / numCols); colWidths = Array(numCols).fill(w); colWidths[numCols - 1] = CONTENT_WIDTH - w * (numCols - 1); }
  const cm = { top: 80, bottom: 80, left: 120, right: 120 };
  const headerRow = new TableRow({ children: headers.map((h, i) => new TableCell({
    borders: BRAND_BORDERS, width: { size: colWidths[i], type: WidthType.DXA },
    shading: { fill: AGENTMAN_100, type: ShadingType.CLEAR }, margins: cm, verticalAlign: VerticalAlign.CENTER,
    children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [
      new TextRun({ text: h, font: FONT_BODY, bold: true, size: 22, color: AGENTMAN_500 })
    ]})]
  }))});
  const bodyRows = data.map((row, ri) => new TableRow({ children: row.map((cell, ci) => new TableCell({
    borders: BRAND_BORDERS, width: { size: colWidths[ci], type: WidthType.DXA },
    shading: { fill: ri % 2 === 0 ? "FFFFFF" : AGENTMAN_50, type: ShadingType.CLEAR },
    margins: cm, verticalAlign: VerticalAlign.CENTER,
    children: [new Paragraph({ alignment: ci === 0 ? AlignmentType.LEFT : AlignmentType.CENTER, children: [
      new TextRun({ text: String(cell), font: FONT_BODY, bold: ci === 0, size: 20, color: CHARCOAL_950 })
    ]})]
  }))}));
  return new Table({ width: { size: CONTENT_WIDTH, type: WidthType.DXA }, columnWidths: colWidths, rows: [headerRow, ...bodyRows] });
}

Stat Block Layout (MANDATORY — Prevents Overlap)

CRITICAL: Never use stacked paragraphs for stat blocks. Always use a Table.

function statRow(statPairs) {
  const n = statPairs.length;
  const colW = Math.floor(CONTENT_WIDTH / n);
  const colWidths = Array(n).fill(colW);
  colWidths[n - 1] = CONTENT_WIDTH - colW * (n - 1);
  const numberRow = new TableRow({ children: statPairs.map(([value], i) => new TableCell({
    borders: NO_BORDERS, width: { size: colWidths[i], type: WidthType.DXA }, verticalAlign: VerticalAlign.CENTER,
    children: [new Paragraph({ alignment: AlignmentType.CENTER, spacing: { before: 100, after: 20 }, children: [
      new TextRun({ text: value, font: FONT_BODY, bold: true, size: 56, color: AGENTMAN_500 })
    ]})]
  }))});
  const labelRow = new TableRow({ children: statPairs.map(([, label], i) => new TableCell({
    borders: NO_BORDERS, width: { size: colWidths[i], type: WidthType.DXA }, verticalAlign: VerticalAlign.CENTER,
    children: [new Paragraph({ alignment: AlignmentType.CENTER, spacing: { before: 0, after: 100 }, children: [
      new TextRun({ text: label, font: FONT_BODY, size: 20, color: SLATE_600 })
    ]})]
  }))});
  return new Table({ width: { size: CONTENT_WIDTH, type: WidthType.DXA }, columnWidths: colWidths, rows: [numberRow, labelRow] });
}

Callout Block

function calloutBlock(heading, body) {
  return new Table({ width: { size: CONTENT_WIDTH, type: WidthType.DXA }, columnWidths: [CONTENT_WIDTH],
    rows: [new TableRow({ children: [new TableCell({
      borders: BRAND_BORDERS, width: { size: CONTENT_WIDTH, type: WidthType.DXA },
      shading: { fill: AGENTMAN_75, type: ShadingType.CLEAR }, margins: { top: 120, bottom: 120, left: 200, right: 200 },
      children: [
        new Paragraph({ children: [new TextRun({ text: heading, font: FONT_BODY, bold: true, size: 24, color: CHARCOAL_950 })] }),
        new Paragraph({ spacing: { before: 60 }, children: [new TextRun({ text: body, font: FONT_BODY, size: 20, color: SLATE_600 })] }),
      ]
    })]})],
  });
}
function agentmanFooter(dateStr) {
  const today = dateStr || new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
  return new Footer({ children: [new Paragraph({
    border: { top: { style: BorderStyle.SINGLE, size: 3, color: AGENTMAN_500, space: 4 } },
    alignment: AlignmentType.CENTER, children: [
      new TextRun({ text: `Agentman Equity Research Assistant · ${today} · Data: FMP    `, font: FONT_BODY, size: 16, color: SLATE_500 }),
      new TextRun({ children: ["Page ", PageNumber.CURRENT], font: FONT_BODY, size: 16, color: SLATE_500 }),
    ]
  })]});
}

function disclaimer() {
  return [ new Paragraph({ children: [] }), terracottaRule(6),
    new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({
      text: "This Agentman research report is for informational purposes only and does not constitute personalized investment advice. Prepared by Agentman Equity Research Assistant.",
      font: FONT_BODY, size: 16, italics: true, color: SLATE_500 })]})
  ];
}

Key Rules

  1. Stat blocks MUST use statRow() table layout — never stacked paragraphs
  2. Always ShadingType.CLEAR — never ShadingType.SOLID
  3. Always WidthType.DXA — never WidthType.PERCENTAGE
  4. Set both columnWidths on Table AND width on each TableCell
  5. Page size US Letter: width 12240, height 15840
  6. Title must start with "Agentman —"
  7. Terracotta budget: max 3 per section
  8. Inter (FONT_HEADING) on all headings (H1, H2, H3) and document title
  9. Calibri (FONT_BODY) on ALL other TextRuns — body, tables, footer, stat blocks, captions
  10. Visual weight: 70% charcoal, 20% neutrals, 10% terracotta
  11. Banned colors: green, amber, red, blue, navy, pure black, Word default blue, generic grays, gradients

Agentman DOCX Style Guide (JavaScript / docx-js)

Agentman brand styling for DOCX report generation using docx-js (JavaScript). Use when generating ANY Word document report. Provides mandatory color constants, table styling helpers, paragraph style builders, and a pre-generation checklist. Ensures every DOCX uses the Agentman warm terracotta/charcoal palette with Inter for headings and Calibri for body text.

designv2.0.0
docxdocx-jsjavascriptwordbrand-guidelinesdesign-systemagentmanequity-research

Included Files

  • SKILL.md(13.3 KB)— shown above

Related skills

Others that solve nearby problems.

Ready to use this skill?

Attach it to your Agentman agents, or clone it and make it yours.

Free to use. Clone this and any other public skill with a free Agentman account — publishing your own is free up to 10 skills.

AgentSkills is more than a copy-paste library

  • Versioned, not pastedEdit a skill once and every agent using it follows the new version.
  • Shared with your teamOne library with per-skill access control and a record of who did what.
  • One connectionA single MCP link works across Claude, ChatGPT, Cursor and Agentman agents.
Explore AgentSkills →

Or try it in an assistant