tear-sheet by anthropics/financial-services-plugins
npx skills add https://github.com/anthropics/financial-services-plugins --skill tear-sheet通过 S&P Global MCP 工具从标普 Capital IQ 提取实时数据,并格式化为专业的 Word 文档,生成面向特定受众的公司摘要报告。
以下是合理的默认设置。如需根据公司品牌进行定制,请修改此部分——常见的更改包括更换调色板、更改字体(Calibri 是许多银行的标准字体)以及更新免责声明文本。
颜色:
排版(docx-js 使用的尺寸单位为半磅):
公司标题横幅:
borders: none 和 shading: none。将列宽各设置为 50%。将左列字段(股票代码、总部、成立年份、员工人数)作为单独的段落放在左侧单元格中。将右列字段(市值、企业价值、股价、流通股数)放在右侧单元格中。每个字段是一个单独的段落:标签使用粗体文本运行,值使用常规文本运行。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
章节标题:
paragraph.borders.bottom = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" } 将下边框应用于章节标题段落。不要使用 doc.addParagraph() 和单独的水平线元素。不要使用 thematicBreak。边框必须位于标题段落本身,且后间距为 0 磅,以便线条紧贴标题文本。项目符号格式:
表格(仅限财务数据):
布局:
数字格式:
页脚(文档页脚,非内联): 将来源归属和免责声明放在实际的文档页脚中(每页重复),而不是作为底部的内联正文文本。页脚在每页上恰好有两行,居中:
你必须使用这些确切的函数来创建文档元素。不要编写自定义的 docx-js 样式代码。 将这些函数复制到生成的 Node 脚本中并调用它们。上面的样式配置说明仍作为文档保留;这些函数是执行机制。
const docx = require("docx");
const {
Document, Paragraph, TextRun, Table, TableRow, TableCell,
WidthType, AlignmentType, BorderStyle, ShadingType,
Header, Footer, PageNumber, HeadingLevel, TableLayoutType,
convertInchesToTwip
} = docx;
// ── 颜色常量 ──
const COLORS = {
PRIMARY: "1F3864",
ACCENT: "2E75B6",
TABLE_HEADER_FILL: "D6E4F0",
TABLE_ALT_ROW: "F2F2F2",
TABLE_BORDER: "CCCCCC",
HEADER_TEXT: "FFFFFF",
FOOTER_TEXT: "666666",
};
const FONT = "Arial";
// ── 1. createHeaderBanner ──
// 返回一个 docx 元素数组:[横幅段落, 键值表格]
function createHeaderBanner(companyName, leftFields, rightFields) {
// leftFields / rightFields: 对象数组 { label: string, value: string }
const banner = new Paragraph({
children: [
new TextRun({
text: companyName,
bold: true,
size: 36, // 18pt
color: COLORS.HEADER_TEXT,
font: FONT,
}),
],
shading: { type: ShadingType.CLEAR, color: "auto", fill: COLORS.PRIMARY },
spacing: { after: 0 },
alignment: AlignmentType.LEFT,
});
function buildCellParagraphs(fields) {
return fields.map(
(f) =>
new Paragraph({
children: [
new TextRun({ text: f.label + " ", bold: true, size: 18, font: FONT }),
new TextRun({ text: f.value, size: 18, font: FONT }),
],
spacing: { after: 40 },
})
);
}
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
const noShading = { type: ShadingType.CLEAR, color: "auto", fill: "FFFFFF" };
const kvTable = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: buildCellParagraphs(leftFields),
width: { size: 50, type: WidthType.PERCENTAGE },
borders: noBorders,
shading: noShading,
}),
new TableCell({
children: buildCellParagraphs(rightFields),
width: { size: 50, type: WidthType.PERCENTAGE },
borders: noBorders,
shading: noShading,
}),
],
}),
],
width: { size: 100, type: WidthType.PERCENTAGE },
});
return [banner, kvTable];
}
// ── 2. createSectionHeader ──
// 返回一个带有下边框线的单个 Paragraph
function createSectionHeader(text) {
return new Paragraph({
children: [
new TextRun({
text: text,
bold: true,
size: 22, // 11pt
color: COLORS.PRIMARY,
font: FONT,
}),
],
spacing: { before: 240, after: 0 }, // 12pt before, 0pt after
border: {
bottom: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
},
});
}
// ── 3. createTable ──
// headers: string[], rows: string[][], options: { accentHeader?, fontSize? }
function createTable(headers, rows, options = {}) {
const fontSize = options.fontSize || 17; // 8.5pt default
const headerFill = options.accentHeader ? COLORS.ACCENT : COLORS.TABLE_HEADER_FILL;
const headerTextColor = options.accentHeader ? COLORS.HEADER_TEXT : "000000";
const cellBorders = {
top: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
bottom: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
left: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
right: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
};
const cellMargins = { top: 40, bottom: 40, left: 80, right: 80 };
function isNumeric(val) {
if (typeof val !== "string") return false;
const cleaned = val.replace(/[,$%()]/g, "").trim();
return cleaned !== "" && !isNaN(cleaned);
}
// 标题行
const headerRow = new TableRow({
children: headers.map(
(h) =>
new TableCell({
children: [
new Paragraph({
children: [
new TextRun({
text: h,
bold: true,
size: fontSize,
color: headerTextColor,
font: FONT,
}),
],
}),
],
shading: { type: ShadingType.CLEAR, color: "auto", fill: headerFill },
borders: cellBorders,
margins: cellMargins,
})
),
});
// 带有交替底纹的数据行
const dataRows = rows.map((row, rowIdx) => {
const fill = rowIdx % 2 === 1 ? COLORS.TABLE_ALT_ROW : "FFFFFF";
return new TableRow({
children: row.map((cell, colIdx) => {
const align = colIdx > 0 && isNumeric(cell)
? AlignmentType.RIGHT
: AlignmentType.LEFT;
return new TableCell({
children: [
new Paragraph({
children: [
new TextRun({ text: cell, size: fontSize, font: FONT }),
],
alignment: align,
}),
],
shading: { type: ShadingType.CLEAR, color: "auto", fill: fill },
borders: cellBorders,
margins: cellMargins,
});
}),
});
});
return new Table({
rows: [headerRow, ...dataRows],
width: { size: 100, type: WidthType.PERCENTAGE },
});
}
// ── 4. createBulletList ──
// items: string[], style: "synthesis" | "informational"
function createBulletList(items, style = "synthesis") {
const indent =
style === "synthesis"
? { left: 360, hanging: 180 } // 360 DXA left, hanging indent for bullet
: { left: 180 }; // 180 DXA, no hanging
return items.map(
(item) =>
new Paragraph({
children: [
new TextRun({ text: "• ", font: FONT, size: 18 }),
new TextRun({ text: item, font: FONT, size: 18 }),
],
indent: indent,
spacing: { after: 60 },
})
);
}
// ── 5. createFooter ──
// date: string (e.g., "February 23, 2026")
function createFooter(date) {
return new Footer({
children: [
new Paragraph({
children: [
new TextRun({
text: `Data: S&P Capital IQ via Kensho | Analysis: AI-generated | ${date}`,
italics: true,
size: 14, // 7pt
color: COLORS.FOOTER_TEXT,
font: FONT,
}),
],
alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [
new TextRun({
text: "For informational purposes only. Not investment advice.",
italics: true,
size: 14,
color: COLORS.FOOTER_TEXT,
font: FONT,
}),
],
alignment: AlignmentType.CENTER,
}),
],
});
}
在生成的脚本中的用法:
createHeaderBanner(...) 而不是手动构建横幅段落和表格createSectionHeader(...)——切勿手动设置段落边框createTable(...)——财务摘要、交易可比公司、并购活动、关系表、融资历史等。对于并购活动表(IB/M&A 模板),传递 { accentHeader: true }。对于非数字表格(例如,关系、所有权),该函数仍然有效——它只对包含数字值的单元格进行右对齐。createBulletList(items, "synthesis")createBulletList(items, "informational")createFooter(date) 传递给 Document 构造函数的 footers.default 属性这些函数消除了以下问题:
ShadingType.CLEAR)border.bottom)borders: none)• 字符)在继续之前收集最多四项内容:
如果用户未指定受众,请询问。
从本技能目录中读取相应的参考文件:
references/equity-research.mdreferences/ib-ma.mdreferences/corp-dev.mdreferences/sales-bd.md每个参考文件都定义了章节、查询计划、格式指南和页面长度默认值。
首先: 创建中间文件目录:
mkdir -p /tmp/tear-sheet/
使用标普全球 MCP 工具(也称为 Kensho LLM-ready API)。Claude 将能够访问用于财务数据、公司信息、市场数据、共识预期、收益电话会议记录、并购交易和业务关系的结构化工具。每个参考文件中的查询计划描述了为每个章节检索哪些数据——将这些映射到对话中可用的适当标普全球工具。
在每个查询步骤之后,立即将检索到的数据写入参考文件查询计划中指定的中间文件。 不要推迟写入——写入磁盘的数据可以防止在长对话中出现上下文退化。
查询策略: 每个参考文件都包含一个包含 4-6 个数据检索步骤的查询计划。这些是起点,而不是严格的约束。优先考虑数据完整性而非最小化调用次数:
用户指定的可比公司: 如果用户提供了可比公司,请明确查询每个可比公司的财务数据和倍数。如果未提供可比公司,请使用工具返回的任何同行数据,或使用竞争对手工具从公司所在行业识别同行。
来自用户的可选上下文: 留意用户自然提供的额外上下文。如果他们提到收购方是谁("我们正在为我们的平台考虑这个")、他们销售什么("我们向银行销售数据分析")或可能的买家是谁("这对 Salesforce 或 Microsoft 来说会很有趣"),请将该上下文纳入相关的综合章节(战略契合度、对话切入点、交易角度)。不要提示此信息——如果提供了就使用它。
私营公司处理: CIQ 包含私营公司数据,因此以相同方式查询。但是,预计结果会更稀疏。为私营公司生成时:
所有数据收集完成且中间文件写入后,在单个专门的步骤中计算所有衍生指标。这是一个仅计算步骤——不进行新的 MCP 查询。
将所有中间文件读回上下文,然后计算:
验证(从算术验证迁移而来): 在此计算步骤中,强制执行所有算术检查:
如果验证失败:尝试根据原始数据重新计算。如果仍然不一致,请将指标标记为 "N/A",而不是发布错误的数字。摘要报告中的无声数学错误会破坏可信度。
将结果写入 /tmp/tear-sheet/calculations.csv,列包括:metric,value,formula,components
示例行:
metric,value,formula,components
gross_margin_fy2024,72.4%,gross_profit/revenue,"9524/13159"
revenue_growth_fy2024,12.3%,(current-prior)/prior,"13159/11716"
net_debt_fy2024,2150,total_debt-cash,"4200-2050"
在生成文档之前,验证所有中间文件是否存在且已填充。
通过单独的读取操作读取每个中间文件并打印验证摘要:
=== Tear Sheet Data Verification ===
company-profile.txt: ✓ (12 fields)
financials.csv: ✓ (36 rows)
segments.csv: ✓ (8 rows)
valuation.csv: ✓ (5 rows)
calculations.csv: ✓ (18 rows)
earnings.txt: ✓ (populated)
relationships.txt: ⚠ MISSING
peer-comps.csv: ✓ (12 rows)
================================
软性关卡: 如果当前受众类型所需的任何文件缺失或为空,请打印警告但继续。摘要报告会优雅地处理缺失数据,使用 "N/A" 并跳过章节。但是,警告确保了对丢失数据的可见性。
关键规则:文件——而不是你对早期对话的记忆——是文档中每个数字的唯一真实来源。 在步骤 4 中生成 DOCX 时,从中间文件中读取值。不要依赖对话上下文获取财务数据。
阅读 /mnt/skills/public/docx/SKILL.md 以了解 docx 创建机制(通过 Node 使用 docx-js)。应用上述样式配置以及参考文件中的章节特定格式。
页面长度默认值(用户可以覆盖):
如果内容超过目标,每个参考文件都指定了首先削减哪些章节。
输出文件名: [公司名称]_TearSheet_[受众]_[YYYYMMDD].docx 示例:Nvidia_TearSheet_CorpDev_20260220.docx
保存到 /mnt/user-data/outputs/ 并呈现给用户。
这些规则覆盖其他所有内容:
从 MCP 工具检索的所有数据必须在文档生成之前持久化到结构化的中间文件中。这些文件——而不是对话上下文——是文档中每个数字的唯一真实来源。
设置: 在步骤 3 开始时,创建工作目录:
mkdir -p /tmp/tear-sheet/
查询后立即写入指令: 每个 MCP 查询步骤完成后,立即将检索到的数据写入适当的中间文件。不要等到所有查询完成。每个参考文件的查询计划指定了每个步骤后要写入哪些文件。
文件模式:
| 文件 | 格式 | 列 / 结构 | 使用方 |
|---|---|---|---|
/tmp/tear-sheet/company-profile.txt | 键值文本 | name, ticker, exchange, HQ, sector, industry, founded, employees, market_cap, enterprise_value, stock_price, 52wk_high, 52wk_low, shares_outstanding, beta, ownership | 全部 |
/tmp/tear-sheet/financials.csv | CSV | period,line_item,value,source | 全部 |
/tmp/tear-sheet/segments.csv | CSV | period,segment_name,revenue,source | ER, IB, CD |
/tmp/tear-sheet/valuation.csv | CSV | metric,trailing,forward,source | ER, IB, CD |
/tmp/tear-sheet/consensus.csv | CSV | metric,fy_year,value,source | ER |
/tmp/tear-sheet/earnings.txt | 结构化文本 | Quarter, date, key quotes, guidance, key drivers | ER, IB, Sales |
/tmp/tear-sheet/relationships.txt | 结构化文本 | Customers, suppliers, partners, competitors — each with descriptors | IB, CD, Sales |
/tmp/tear-sheet/peer-comps.csv | CSV | ticker,metric,value,source | ER, IB, CD |
/tmp/tear-sheet/ma-activity.csv | CSV | date,target,deal_value,type,rationale,source | IB, CD |
/tmp/tear-sheet/calculations.csv | CSV | metric,value,formula,components | 全部(在步骤 3b 中写入) |
缩写: ER = 股票研究, IB = 投行/并购, CD = 企业发展, Sales = 销售/业务拓展。
并非每种受众类型都使用每个文件——参考文件定义了哪些查询步骤适用。与当前受众类型无关的文件无需创建。
仅原始值。 中间文件存储工具返回的原始值。不要在这些文件中预先计算利润率、增长率或其他衍生指标——这将在步骤 3b 中进行。
页面预算执行: 每个参考文件都指定了默认页面长度和编号的削减顺序。如果渲染的文档超过目标,请按指定的顺序应用削减——不要尝试将字体大小或页边距缩小到模板最小值以下。削减顺序是一个严格的优先级堆栈:在触及章节 2 之前完全削减章节 1。
→ 算术验证现在在步骤 3b(计算衍生指标)中强制执行。 所有利润率计算、增长率、细分市场总计、百分比列和估值交叉检查都在专门的计算步骤中进行验证,然后才开始文档生成。完整的验证清单请参见步骤 3b。
每周安装次数
46
仓库
GitHub 星标
5.6K
首次出现
13 天前
安全审计
安装于
opencode45
gemini-cli45
github-copilot45
amp45
codex45
kimi-cli45
Generate audience-specific company tear sheets by pulling live data from S&P Capital IQ via the S&P Global MCP tools and formatting the result as a professional Word document.
These are sensible defaults. To customize for your firm's brand, modify this section — common changes include swapping the color palette, changing the font (Calibri is standard at many banks), and updating the disclaimer text.
Colors:
Typography (sizes in half-points for docx-js):
Company Header Banner:
borders: none and shading: none on all cells. Set column widths to 50% each. Place left-column fields (ticker, HQ, founded, employees) as separate paragraphs in the left cell. Place right-column fields (market cap, EV, stock price, shares outstanding) in the right cell. Each field is a single paragraph: bold run for the label, regular run for the value.Section Headers:
paragraph.borders.bottom = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" }. Do not use doc.addParagraph() with a separate horizontal rule element. Do not use thematicBreak. The border must be on the heading paragraph itself with 0pt spacing after, so the rule sits tight against the header text.Bullet Formatting:
Tables (financial data only):
Layout:
Number formatting:
Footer (document footer, not inline): Place the source attribution and disclaimer in the actual document footer (repeated on every page), not as inline body text at the bottom. The footer is exactly two lines, centered, on every page:
You MUST use these exact functions to create document elements. Do NOT write custom docx-js styling code. Copy these functions into your generated Node script and call them. The Style Configuration prose above remains as documentation; these functions are the enforcement mechanism.
const docx = require("docx");
const {
Document, Paragraph, TextRun, Table, TableRow, TableCell,
WidthType, AlignmentType, BorderStyle, ShadingType,
Header, Footer, PageNumber, HeadingLevel, TableLayoutType,
convertInchesToTwip
} = docx;
// ── Color constants ──
const COLORS = {
PRIMARY: "1F3864",
ACCENT: "2E75B6",
TABLE_HEADER_FILL: "D6E4F0",
TABLE_ALT_ROW: "F2F2F2",
TABLE_BORDER: "CCCCCC",
HEADER_TEXT: "FFFFFF",
FOOTER_TEXT: "666666",
};
const FONT = "Arial";
// ── 1. createHeaderBanner ──
// Returns an array of docx elements: [banner paragraph, key-value table]
function createHeaderBanner(companyName, leftFields, rightFields) {
// leftFields / rightFields: arrays of { label: string, value: string }
const banner = new Paragraph({
children: [
new TextRun({
text: companyName,
bold: true,
size: 36, // 18pt
color: COLORS.HEADER_TEXT,
font: FONT,
}),
],
shading: { type: ShadingType.CLEAR, color: "auto", fill: COLORS.PRIMARY },
spacing: { after: 0 },
alignment: AlignmentType.LEFT,
});
function buildCellParagraphs(fields) {
return fields.map(
(f) =>
new Paragraph({
children: [
new TextRun({ text: f.label + " ", bold: true, size: 18, font: FONT }),
new TextRun({ text: f.value, size: 18, font: FONT }),
],
spacing: { after: 40 },
})
);
}
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
const noShading = { type: ShadingType.CLEAR, color: "auto", fill: "FFFFFF" };
const kvTable = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: buildCellParagraphs(leftFields),
width: { size: 50, type: WidthType.PERCENTAGE },
borders: noBorders,
shading: noShading,
}),
new TableCell({
children: buildCellParagraphs(rightFields),
width: { size: 50, type: WidthType.PERCENTAGE },
borders: noBorders,
shading: noShading,
}),
],
}),
],
width: { size: 100, type: WidthType.PERCENTAGE },
});
return [banner, kvTable];
}
// ── 2. createSectionHeader ──
// Returns a single Paragraph with bottom border rule
function createSectionHeader(text) {
return new Paragraph({
children: [
new TextRun({
text: text,
bold: true,
size: 22, // 11pt
color: COLORS.PRIMARY,
font: FONT,
}),
],
spacing: { before: 240, after: 0 }, // 12pt before, 0pt after
border: {
bottom: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
},
});
}
// ── 3. createTable ──
// headers: string[], rows: string[][], options: { accentHeader?, fontSize? }
function createTable(headers, rows, options = {}) {
const fontSize = options.fontSize || 17; // 8.5pt default
const headerFill = options.accentHeader ? COLORS.ACCENT : COLORS.TABLE_HEADER_FILL;
const headerTextColor = options.accentHeader ? COLORS.HEADER_TEXT : "000000";
const cellBorders = {
top: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
bottom: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
left: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
right: { style: BorderStyle.SINGLE, size: 1, color: COLORS.TABLE_BORDER },
};
const cellMargins = { top: 40, bottom: 40, left: 80, right: 80 };
function isNumeric(val) {
if (typeof val !== "string") return false;
const cleaned = val.replace(/[,$%()]/g, "").trim();
return cleaned !== "" && !isNaN(cleaned);
}
// Header row
const headerRow = new TableRow({
children: headers.map(
(h) =>
new TableCell({
children: [
new Paragraph({
children: [
new TextRun({
text: h,
bold: true,
size: fontSize,
color: headerTextColor,
font: FONT,
}),
],
}),
],
shading: { type: ShadingType.CLEAR, color: "auto", fill: headerFill },
borders: cellBorders,
margins: cellMargins,
})
),
});
// Data rows with alternating shading
const dataRows = rows.map((row, rowIdx) => {
const fill = rowIdx % 2 === 1 ? COLORS.TABLE_ALT_ROW : "FFFFFF";
return new TableRow({
children: row.map((cell, colIdx) => {
const align = colIdx > 0 && isNumeric(cell)
? AlignmentType.RIGHT
: AlignmentType.LEFT;
return new TableCell({
children: [
new Paragraph({
children: [
new TextRun({ text: cell, size: fontSize, font: FONT }),
],
alignment: align,
}),
],
shading: { type: ShadingType.CLEAR, color: "auto", fill: fill },
borders: cellBorders,
margins: cellMargins,
});
}),
});
});
return new Table({
rows: [headerRow, ...dataRows],
width: { size: 100, type: WidthType.PERCENTAGE },
});
}
// ── 4. createBulletList ──
// items: string[], style: "synthesis" | "informational"
function createBulletList(items, style = "synthesis") {
const indent =
style === "synthesis"
? { left: 360, hanging: 180 } // 360 DXA left, hanging indent for bullet
: { left: 180 }; // 180 DXA, no hanging
return items.map(
(item) =>
new Paragraph({
children: [
new TextRun({ text: "• ", font: FONT, size: 18 }),
new TextRun({ text: item, font: FONT, size: 18 }),
],
indent: indent,
spacing: { after: 60 },
})
);
}
// ── 5. createFooter ──
// date: string (e.g., "February 23, 2026")
function createFooter(date) {
return new Footer({
children: [
new Paragraph({
children: [
new TextRun({
text: `Data: S&P Capital IQ via Kensho | Analysis: AI-generated | ${date}`,
italics: true,
size: 14, // 7pt
color: COLORS.FOOTER_TEXT,
font: FONT,
}),
],
alignment: AlignmentType.CENTER,
}),
new Paragraph({
children: [
new TextRun({
text: "For informational purposes only. Not investment advice.",
italics: true,
size: 14,
color: COLORS.FOOTER_TEXT,
font: FONT,
}),
],
alignment: AlignmentType.CENTER,
}),
],
});
}
Usage in generated scripts:
createHeaderBanner(...) instead of manually building banner paragraphs and tablescreateSectionHeader(...) for every section title — never manually set paragraph borderscreateTable(...) for all tabular data — financial summaries, trading comps, M&A activity, relationship tables, funding history, etc. Pass { accentHeader: true } for M&A activity tables (IB/M&A template). For non-numeric tables (e.g., relationships, ownership), the function still works correctly — it only right-aligns cells that contain numeric values.createBulletList(items, "synthesis") for earnings highlights, strategic fit, integration considerations, and conversation starterscreateBulletList(items, "informational") for relationship entriescreateFooter(date) to the Document constructor's propertyWhat these functions eliminate:
ShadingType.CLEAR everywhere)border.bottom on the paragraph itself)borders: none)• character only)Gather up to four things before proceeding:
If the user doesn't specify an audience, ask.
Read the corresponding reference file from this skill's directory:
references/equity-research.mdreferences/ib-ma.mdreferences/corp-dev.mdreferences/sales-bd.mdEach reference defines sections, a query plan, formatting guidance, and page length defaults.
First: Create the intermediate file directory:
mkdir -p /tmp/tear-sheet/
Use the S &P Global MCP tools (also known as the Kensho LLM-ready API). Claude will have access to structured tools for financial data, company information, market data, consensus estimates, earnings transcripts, M&A transactions, and business relationships. The query plans in each reference file describe what data to retrieve for each section — map these to the appropriate S&P Global tools available in the conversation.
After each query step, immediately write the retrieved data to the intermediate file(s) specified in the reference file's query plan. Do not defer writes — data written to disk is protected from context degradation in long conversations.
Query strategy: Each reference file includes a query plan with 4-6 data retrieval steps. These are starting points, not rigid constraints. Prioritize data completeness over minimizing calls:
User-specified comps: If the user provided comparable companies, query financials and multiples for each comp explicitly. If no comps were provided, use whatever peer data the tools return, or identify peers from the company's sector using the competitors tool.
Optional context from the user: Listen for additional context the user provides naturally. If they mention who the acquirer is ("we're looking at this for our platform"), what they sell ("we sell data analytics to banks"), or who the likely buyers are ("this would be interesting to Salesforce or Microsoft"), incorporate that context into the relevant synthesis sections (Strategic Fit, Conversation Starters, Deal Angle). Don't prompt for this information — just use it if offered.
Private company handling: CIQ includes private company data, so query the same way. However, expect sparser results. When generating for a private company:
After all data collection is complete and intermediate files are written, compute all derived metrics in a single dedicated pass. This is a calculation-only step — no new MCP queries.
Read all intermediate files back into context , then compute:
Validation (moved from Arithmetic Validation): During this calculation pass, enforce all arithmetic checks:
If a validation fails: attempt recalculation from raw data. If still inconsistent, flag the metric as "N/A" rather than publishing incorrect numbers. Quiet math errors in a tear sheet destroy credibility.
Write results to /tmp/tear-sheet/calculations.csv with columns: metric,value,formula,components
Example rows:
metric,value,formula,components
gross_margin_fy2024,72.4%,gross_profit/revenue,"9524/13159"
revenue_growth_fy2024,12.3%,(current-prior)/prior,"13159/11716"
net_debt_fy2024,2150,total_debt-cash,"4200-2050"
Before generating the document, verify that all intermediate files are present and populated.
Read each intermediate file via separate read operations and print a verification summary:
=== Tear Sheet Data Verification ===
company-profile.txt: ✓ (12 fields)
financials.csv: ✓ (36 rows)
segments.csv: ✓ (8 rows)
valuation.csv: ✓ (5 rows)
calculations.csv: ✓ (18 rows)
earnings.txt: ✓ (populated)
relationships.txt: ⚠ MISSING
peer-comps.csv: ✓ (12 rows)
================================
Soft gate: If any file expected for the current audience type is missing or empty, print a warning but continue. The tear sheet handles missing data gracefully with "N/A" and section skipping. However, the warning ensures visibility into what data was lost.
Critical rule: The files — not your memory of earlier conversation — are the single source of truth for every number in the document. When generating the DOCX in Step 4, read values from the intermediate files. Do not rely on conversation context for financial data.
Read /mnt/skills/public/docx/SKILL.md for docx creation mechanics (docx-js via Node). Apply the Style Configuration above plus the section-specific formatting in the reference file.
Page length defaults (user can override):
If content exceeds the target, each reference file specifies which sections to cut first.
Output filename: [CompanyName]_TearSheet_[Audience]_[YYYYMMDD].docx Example: Nvidia_TearSheet_CorpDev_20260220.docx
Save to /mnt/user-data/outputs/ and present to the user.
These override everything else:
All data retrieved from MCP tools must be persisted to structured intermediate files before document generation. These files — not conversation context — are the single source of truth for every number in the document.
Setup: At the start of Step 3, create the working directory:
mkdir -p /tmp/tear-sheet/
Write-after-query mandate: After each MCP query step completes, immediately write the retrieved data to the appropriate intermediate file(s). Do not wait until all queries finish. Each reference file's query plan specifies which file(s) to write after each step.
File schemas:
| File | Format | Columns / Structure | Used By |
|---|---|---|---|
/tmp/tear-sheet/company-profile.txt | Key-value text | name, ticker, exchange, HQ, sector, industry, founded, employees, market_cap, enterprise_value, stock_price, 52wk_high, 52wk_low, shares_outstanding, beta, ownership | All |
/tmp/tear-sheet/financials.csv | CSV | period,line_item,value,source | All |
/tmp/tear-sheet/segments.csv | CSV | period,segment_name,revenue,source |
Abbreviations: ER = Equity Research, IB = IB/M&A, CD = Corp Dev, Sales = Sales/BD.
Not every audience type uses every file — the reference files define which query steps apply. Files not relevant to the current audience type need not be created.
Raw values only. Intermediate files store raw values as returned by the tools. Do not pre-compute margins, growth rates, or other derived metrics in these files — that happens in Step 3b.
Page budget enforcement: Each reference file specifies a default page length and a numbered cut order. If the rendered document exceeds the target, apply cuts in the order specified — do not attempt to shrink font sizes or margins below the template minimums. The cut order is a strict priority stack: cut section 1 completely before touching section 2.
→ Arithmetic validation is now enforced in Step 3b (Calculate Derived Metrics). All margin calculations, growth rates, segment totals, percentage columns, and valuation cross-checks are validated during the dedicated calculation pass, before document generation begins. See Step 3b for the full validation checklist.
Weekly Installs
46
Repository
GitHub Stars
5.6K
First Seen
13 days ago
Security Audits
Gen Agent Trust HubPassSocketWarnSnykPass
Installed on
opencode45
gemini-cli45
github-copilot45
amp45
codex45
kimi-cli45
Skills CLI 使用指南:AI Agent 技能包管理器安装与管理教程
46,600 周安装
footers.default| ER, IB, CD |
/tmp/tear-sheet/valuation.csv | CSV | metric,trailing,forward,source | ER, IB, CD |
/tmp/tear-sheet/consensus.csv | CSV | metric,fy_year,value,source | ER |
/tmp/tear-sheet/earnings.txt | Structured text | Quarter, date, key quotes, guidance, key drivers | ER, IB, Sales |
/tmp/tear-sheet/relationships.txt | Structured text | Customers, suppliers, partners, competitors — each with descriptors | IB, CD, Sales |
/tmp/tear-sheet/peer-comps.csv | CSV | ticker,metric,value,source | ER, IB, CD |
/tmp/tear-sheet/ma-activity.csv | CSV | date,target,deal_value,type,rationale,source | IB, CD |
/tmp/tear-sheet/calculations.csv | CSV | metric,value,formula,components | All (written in Step 3b) |