Agentman DOCX Style Guide (JavaScript / docx-js)

Applies Agentman colors, table styles and typography to Word reports generated with docx-js.

Free plan: up to 10 private skills and 3 members, no credit card.

What this skill does

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.

  • docx
  • docx-js
  • javascript
  • word
  • brand-guidelines
  • design-system
  • agentman
  • equity-research

SKILL.md

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

A skill is a written procedure, not a prompt. Add it to your library and it is yours to edit in plain language.

In your Agentman library

A skill you keep, not a prompt you paste

  • Versioned, not pasted

    Edit a skill once and every agent using it follows the new version.

  • Shared with your team

    One library with per-skill access control and a record of who did what.

  • One connection

    A single MCP link works across Claude, ChatGPT, Cursor and Agentman agents.

How Agent Skills work
Questions

Using a skill

What is a skill?

A skill is a written procedure an AI assistant reads before it does a task: the steps, the judgement calls, the standards and what "done" looks like. It lives in a SKILL.md file, sometimes with reference files beside it. A prompt is typed again each time; a skill is versioned and runs the same way for everyone who uses it.

How do I use this skill in Claude, ChatGPT or Agentman?

Create a free Agentman account and add the skill to your library. One MCP connection then makes your library available in Claude, ChatGPT, Cursor and Agentman agents, so you set it up once. To see it work before you sign up, use "Try it in Claude" on this page, which opens a chat that reads the public SKILL.md.

Can I edit the skill after I add it?

Yes. Cloning a skill puts a copy in your workspace that is yours to edit in plain language. Skills are versioned: edit one once and every agent using it follows the new version. The free plan covers up to 10 private skills and 3 members, no credit card.

Make it yours

Add Agentman DOCX Style Guide (JavaScript / docx-js) to your library, edit it to match how your team works, and use it from Claude, ChatGPT or an Agentman agent. The free plan covers up to 10 private skills and 3 members, no credit card.

Or try it in ChatGPT first.

Use this skill free