[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69A3GSMHMNPKWVHHF02BC0W":3,"skill-same:01M69A3GSMHMNPKWVHHF02BC0W":64},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":20,"linkOnly":21,"markdownContent":22,"markdownBody":23,"frontMatter":24,"refName":25,"commitSha":26,"skillDirectory":27,"forksCount":28,"repositoryHtmlUrl":29,"repositoryHomepageUrl":6,"ownerHtmlUrl":30,"ownerBlogUrl":31,"ownerDescription":32,"updatedAt":33,"install":34,"resources":47},"01M69A3GSMHMNPKWVHHF02BC0W","competitive-battlecard-builder",null,"Build an interactive sales battlecard comparing your product or company against one or more named competitors: their likely pitch, where you win, where you're genuinely vulnerable, and objection-handling scripts. Use this skill whenever someone asks for a battlecard, competitive positioning, \"how do we beat [competitor]\", objection handling against a named competitor, or a sales enablement comparison asset. Also use when someone wants to add a competitor to an existing battlecard, update it with new intel, or rescope\u002Ffilter an existing one down to specific competitors or categories.","microsoft\u002Fcat-agent-skills","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69A1AHE1MABT7GVP5143M5Z",105,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fcompetitive-battlecard-builder\u002FSKILL.md",[],[19],"competitive battlecard builder","2026-11-10 10:12:30",false,"---\nname: competitive-battlecard-builder\ndescription: >-\n  Build an interactive sales battlecard comparing your product or company\n  against one or more named competitors: their likely pitch, where you win,\n  where you're genuinely vulnerable, and objection-handling scripts. Use this\n  skill whenever someone asks for a battlecard, competitive positioning, \"how\n  do we beat [competitor]\", objection handling against a named competitor, or\n  a sales enablement comparison asset. Also use when someone wants to add a\n  competitor to an existing battlecard, update it with new intel, or\n  rescope\u002Ffilter an existing one down to specific competitors or categories.\n---\n\n# Competitive Battlecard Builder\n\n## Compatibility\n\nThe interactive app output and the PowerPoint export both require Code Interpreter available to the agent. Without it, present the battlecard as formatted chat text instead.\n\nWhen writing Python for Code Interpreter, avoid building scripts through shell heredocs or string concatenation that has to escape the battlecard data inline, especially once brand colors, hex codes, or parenthesized function calls like `RGBColor(...)` are involved, unbalanced quotes or parens in the data will break the heredoc before your code ever runs. Write the JSON data to its own file (`json.dump`) and have the script `json.load` it, or write the whole script as a single Python file directly rather than assembling it through shell quoting.\n\n## Step 1: Gather the inputs\n\n1. **Which kind of comparison this is** — ask first, before anything else: is this a sales battlecard advocating for one specific product against competitors, or a neutral, unbiased comparison with no side taken? This determines the entire shape of what you build next, not just the wording:\n   - **Battlecard**: one product is \"ours,\" the rest are competitors, compared pairwise (us vs. each one). Objection handling applies (Step 3's `objections` array), since that's inherently a sales concept, what a prospect pushes back with and how to respond.\n   - **Neutral comparison**: there is no \"ours.\" Every item is a peer, laid out as one shared matrix, all items as columns, one set of dimensions as rows, no anchor item privileged over the others. Never use the word \"competitor\" anywhere in this mode, not in what you say to the person, not in the data. Skip objection handling entirely, there's no seller position to defend. Gather comparably balanced detail on every item.\n2. **The items being compared** — for a battlecard: your product\u002Fcompany (name and a one-line positioning statement) plus one or more competitors. For a neutral comparison: two or more named items, genuinely peers, no ordering implication.\n3. **What the person already knows** — pitch, strengths, weaknesses for each item, and (battlecard only) objections prospects commonly raise. This is the primary source of truth. Ask for it directly rather than assuming you already know the landscape.\n4. **Permission to supplement with web research** (optional) — ask whether they'd like you to search for additional public information (pricing pages, marketing claims, review sites) to round out gaps. If they decline, work only from what they gave you.\n\nOptionally ask for **category tags** (e.g. Pricing, Security, Integration, Support, Ecosystem) if they want the comparison filterable by category as well as by item. If they don't have a preference, pick 3-5 sensible categories based on the content itself.\n\n5. **Output format** — ask directly: an interactive HTML app (filterable, searchable, with a live scorecard tally, opens in any browser), or a PowerPoint deck (easy to present or drop into an existing deck)? Build only the one they ask for. Don't generate both by default, and don't guess, this is a real fork in what Step 4 does next.\n6. **Branding** — ask whether they have a PowerPoint branding template or brand guidelines (colors, fonts, logo) this should follow. If yes, ask for either the actual `.pptx` template file, or at minimum the core details: primary\u002Fsecondary colors (hex if they have them), font names, and a logo if relevant. If no, say plainly that you'll use a clean, generic default, and move on, this isn't worth belaboring if they don't have one.\n\n## Step 2: The credibility rule (read this before writing anything)\n\nA battlecard a sales rep can't trust is worse than no battlecard, it gets someone caught flat-footed in front of a customer. Every single claim in this skill's output carries a `source` (`\"user\"` or `\"web\"`) and a `verified` flag:\n\n- Anything the person told you directly → `source: \"user\"`, `verified: true`.\n- Anything you found through web research → `source: \"web\"`, `verified: false` until the person explicitly confirms it.\n\n**Never invent a competitor claim.** If you don't have it from the person or a real search result, leave it out rather than filling the gap with something plausible-sounding.\n\nBefore generating the final deliverable, if there are any `web`-sourced claims, show the person exactly what you found, don't summarize or paraphrase it away. For each one, list which competitor it's about, which section it would go in (pitch, win, vulnerability, or objection), and the claim text itself, close to verbatim from the source. End with something low-friction like \"Let me know if anything's off and I'll fix it before it goes into the [html app \u002F deck].\" A person should be able to skim the list and immediately spot anything wrong, not have to reconstruct what you actually found from a vague summary.\n\nOnly after that review does a claim get treated as trustworthy in the shipped battlecard. Claims that remain unconfirmed (the person didn't respond to that specific one, or explicitly said \"not sure\") should either be dropped or clearly marked `unverified` in the output, never silently presented as settled fact. If they flag something as wrong, correct or remove it and don't carry it into Step 3 as-is.\n\n## Step 3: Build the data model\n\nThe two modes need genuinely different data shapes, not just different labels on the same one. Battlecard mode is inherently pairwise, \"us\" versus each competitor, one at a time. Neutral mode is not: there's no anchor item, every item being compared is a peer, so the data model is a real matrix, one shared set of dimensions with a value for every item, not a series of us-vs-X comparisons.\n\n### If `mode` is `\"battlecard\"`\n\n```json\n{\n  \"mode\": \"battlecard\",\n  \"us\": { \"name\": \"string\", \"tagline\": \"string\" },\n  \"categories\": [\"Pricing\", \"Security\", \"...\"],\n  \"competitors\": [\n    {\n      \"id\": \"slug\",\n      \"name\": \"Display name\",\n      \"theirPitch\": { \"text\": \"string\", \"source\": \"user|web\", \"verified\": true },\n      \"comparison\": [\n        {\n          \"category\": \"one of categories[]\",\n          \"dimension\": \"Short label for what's being compared, e.g. 'Recording capture method'\",\n          \"us\": { \"text\": \"Our position on this dimension\", \"source\": \"user|web\", \"verified\": true },\n          \"them\": { \"text\": \"Their position on this dimension\", \"source\": \"user|web\", \"verified\": true },\n          \"edge\": \"us|them|even\"\n        }\n      ],\n      \"objections\": [\n        { \"objection\": \"What the prospect says\", \"response\": \"How to handle it\", \"source\": \"user|web\", \"verified\": true }\n      ]\n    }\n  ]\n}\n```\n\n### If `mode` is `\"neutral\"`\n\n```json\n{\n  \"mode\": \"neutral\",\n  \"items\": [\n    { \"id\": \"slug\", \"name\": \"Display name\", \"positioning\": { \"text\": \"string\", \"source\": \"user|web\", \"verified\": true } }\n  ],\n  \"categories\": [\"Pricing\", \"Security\", \"...\"],\n  \"matrix\": [\n    {\n      \"category\": \"one of categories[]\",\n      \"dimension\": \"Short label for what's being compared\",\n      \"values\": {\n        \"item-id-1\": { \"text\": \"That item's position on this dimension\", \"source\": \"user|web\", \"verified\": true },\n        \"item-id-2\": { \"text\": \"That item's position on this dimension\", \"source\": \"user|web\", \"verified\": true }\n      },\n      \"best\": \"item-id-of-whichever-item-leads-this-dimension, or null if genuinely even\"\n    }\n  ]\n}\n```\n\n`items` needs at least two entries, `values` on every `matrix` row should have an entry for every item in `items` (missing ones render as blank\u002Funverified rather than breaking anything, but that means incomplete data, go back and fill the gap). Never use the word \"competitor\" anywhere in neutral-mode language, in what you say to the person or in the data itself, there isn't one, that's the whole point of this mode.\n\n### Applies to both modes\n\n- **Set `mode` from what they told you in Step 1.** This isn't cosmetic, it changes both the data shape above and how the app renders and labels everything.\n- **Be honest about who leads a dimension** (`edge` in battlecard mode, `best` in neutral mode). Not every dimension has to favor the same side, and \"even\" \u002F `null` is a legitimate, credible answer, not a cop-out. A card where every single row favors the same item reads as marketing, not something anyone can trust.\n- **`category` must match one of the top-level `categories[]` values exactly**, the filtering keys off this.\n- **Order `comparison`\u002F`matrix` rows grouped by category**, all rows for one category together before moving to the next. The app inserts a category header whenever the category changes from the row before it, not once per unique category, so an interleaved order (Pricing, Security, Pricing) produces a duplicate \"Pricing\" header instead of one clean group.\n- Keep every `text`\u002F`response` field a plain sentence a rep could say out loud, not a bullet fragment.\n- `id` values must be unique, stable slugs (used for filtering and DOM references, not shown to the user).\n- Aim for real coverage: if the person only gave you two comparison points, ask for a few more before calling it done. A one-row \"comparison\" isn't a battlecard.\n\n## Step 4a: If they chose HTML\n\nUsing Code Interpreter:\n\n1. Read `assets\u002Fbattlecard-app-template.html`.\n2. **If the person gave you brand colors**, update the CSS custom properties near the top of the `\u003Cstyle>` block (`--blue`, `--green`, `--red`, `--amber`, and their `-light` companions) to match. In battlecard mode, `--green`\u002F`--red` drive the win\u002Floss highlighting, keep those two visually distinct from each other regardless of the brand palette, the highlighting only works if someone can tell the colors apart at a glance. In neutral mode the matrix only ever uses `--blue` for its single neutral highlight, there's no red\u002Fgreen to worry about there. If they gave you a logo, you can add it next to the title, but don't restructure the layout to accommodate it. If they didn't give you branding, leave the defaults as-is, don't invent a color scheme.\n3. Serialize the JSON from Step 3 and escape it for safe embedding: replace every `\u003C\u002F` with `\u003C\\\u002F` before inserting it. This prevents an item name or claim containing the literal text `\u003C\u002Fscript` from prematurely closing the embedded `\u003Cscript>` tag.\n   ```python\n   import json\n   json_str = json.dumps(battlecard_data).replace(\"\u003C\u002F\", \"\u003C\\\\\u002F\")\n   ```\n4. Replace the placeholder token `{{BATTLECARD_DATA_JSON}}` in the template with the escaped string.\n5. Save and return the completed file as a downloadable `.html`, named after what's being compared (e.g. `battlecard-vs-competitor-name.html` for battlecard mode, `comparison-item-a-vs-item-b.html` for neutral mode).\n\nDo this through Code Interpreter so it comes back as an actual file with a download link, printing raw HTML into the chat as text won't render it, the platform will just show the literal markup. If Code Interpreter isn't available, present the same content as formatted chat text instead: battlecard mode grouped by competitor with us\u002Fthem pairs per dimension plus objection handling; neutral mode as a single table with one row per dimension and one column per item.\n\nIf the person later wants a PowerPoint version too, that's a separate follow-up ask, go to Step 4b at that point using the same underlying data.\n\n## Step 4b: If they chose PowerPoint\n\n**If the person gave you a `.pptx` branding template**, open it as the base presentation (`Presentation(\"their_template.pptx\")` in `python-pptx`) and add your slides using their existing slide masters and layouts, so the deck inherits their fonts, colors, and placeholder positions rather than starting from a blank default. Only fall back to building a plain default presentation if they didn't provide one.\n\n**If they only gave you colors\u002Ffonts (no template file)**, apply those to a default layout: use their font for headings and body text, and use their primary\u002Fsecondary colors for the title slide and the \"who has the edge\" table highlighting, keeping the win\u002Floss colors visually distinct from each other regardless of the palette.\n\n**A `python-pptx` gotcha worth knowing before you hit it**: `RGBColor` is a tuple subclass, it has no `.red`\u002F`.green`\u002F`.blue` attributes, indexing into it (`color[0]`, `color[1]`, `color[2]`) works but attribute access throws. If you need a lighter tint of a brand color for cell shading (the same idea as the HTML app's `-light` color variants), do the math on plain integers before constructing the `RGBColor`, not after:\n```python\ndef lighten(hex_color, amount=0.85):\n    hex_color = hex_color.lstrip('#')\n    r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)\n    r = int(r + (255 - r) * amount)\n    g = int(g + (255 - g) * amount)\n    b = int(b + (255 - b) * amount)\n    return RGBColor(r, g, b)\n```\n\n**If they gave you neither**, use a clean, professional default, don't invent a brand identity, just keep it legible and uncluttered.\n\n**Don't ship a bare `python-pptx` table dump.** Left unstyled, `python-pptx` tables render with PowerPoint's default banded-blue table style, small unstyled text, and no visual hierarchy, that reads as an obvious AI-generated afterthought, not something a rep would actually present from. Override the defaults explicitly:\n\n- **Header row**: solid fill (brand primary color, or a dark navy\u002Fslate if no branding was given), white or light text, bold, 12-13pt.\n- **Body cells**: at least 12pt, don't leave `python-pptx`'s tiny default size.\n- **Category-group rows** need their own distinct fill (a light tint of the brand color, or a neutral gray), not plain text at the same visual weight as the data rows below them, otherwise they don't read as section breaks at all.\n- **The title slide needs actual visual weight**: a large bold title (32pt+) and a filled background shape or color block behind it, not plain black text centered on a blank white slide.\n- **Never use a thin accent stripe or colored bar as the only decoration** (a header\u002Ffooter bar spanning the slide, a vertical sidebar stripe, a single-edge border on a box), that's a hallmark of low-effort AI-generated slides. If a card or section needs visual separation, use a filled shape or a solid background tint instead, not a thin line.\n- Keep consistent margins (0.5\"+ from every slide edge) and consistent spacing between elements, don't let gaps vary randomly from slide to slide.\n- Check cell text length against column width before finalizing. If text would wrap past 3-4 lines in a cell, that's a sign to split the competitor across two slides (see the density note below), not to keep shrinking the font until it's unreadable.\n\nIf your sandbox can render the deck to an image before finalizing (for example, converting to PDF and rasterizing a page), do a quick visual pass for text overflow, overlap, or low-contrast text against its background before calling it done. If that's not available in your environment, apply the sizing and wrapping guidance above defensively, since you won't get a check on the actual output.\n\nStructure it depends on which mode you're in, since the data shapes are genuinely different (see Step 3):\n\n**In `\"battlecard\"` mode**: a title slide (your name vs the competitor(s)), then for each competitor, a comparison table with three columns (Dimension \u002F Us \u002F Them), one row per `comparison` entry, the `edge` winner's cell given a background fill, green tint for `\"us\"`, red tint for `\"them\"`, matching the HTML app's convention. Follow each competitor's table with a short objection-handling section (still text, that one doesn't fit a table format).\n\n**In `\"neutral\"` mode**: a title slide, then one single matrix table, not a table per item. Columns are Dimension plus one column per entry in `items[]`, in the same order every time. Rows come from `matrix[]`, one row per dimension, and the `best` item's cell (if any) gets a single neutral accent fill, never red\u002Fgreen, since that would imply a bias that isn't there. If `best` is `null` for a row, no cell gets highlighted. There's no objection-handling section in this mode, there's no `objections` data to draw from.\n\nIn either mode, group rows by category if there are more than 5-6 comparison points, so it doesn't read as a wall of rows. If the matrix or a competitor's table has enough entries that one table would be unreadably dense (this gets tighter fast in neutral mode as `items[]` grows, a 5-column matrix eats width quickly), split by category across multiple slides rather than shrinking text to fit or cramming columns narrower than they can hold their content.\n\n**Carry the verification status into the deck.** Any cell built from a claim with `verified: false` needs a visible marker in the cell text itself (append \"(unverified)\"), whether that's a `us`\u002F`them` cell in battlecard mode or an item's cell in the neutral matrix. Don't drop the distinction just because it's no longer in an app with a badge, someone presenting from a printed or shared deck needs the same warning the HTML app gives them.\n\nIf the person later asks for a rescoped version (\"just give me a deck for these two\" or \"only the pricing category\"), regenerate a fresh `.pptx` from the same underlying data filtered to their request rather than trying to edit the existing file.\n\nIf the person later wants the HTML version too, that's a separate follow-up ask, go to Step 4a at that point using the same underlying data.\n\n## A few things to keep in mind\n\n- If someone wants to add an item (a competitor in battlecard mode, another peer in neutral mode) or update intel on an existing comparison, don't rebuild from scratch, extend the existing JSON data and regenerate whichever output format(s) they're using. Run any new claims through the Step 2 verification gate the same as the first time.\n- If the person names an item you have no information about and declines web research, say so plainly and ask them to supply at least the basics (their positioning, and a couple of real comparison points) rather than fabricating a plausible-sounding profile.\n- If they hand you branding partway through an existing session, regenerate the output with it applied rather than trying to patch colors into an already-delivered file.\n- Keep tone factual throughout. In battlecard mode, this is a tool someone reads on a call, not a marketing piece. In neutral mode, this doubles: no marketing tone, and no accidental advocacy for whichever item happened to get gathered first.\n","# Competitive Battlecard Builder\n\n## Compatibility\n\nThe interactive app output and the PowerPoint export both require Code Interpreter available to the agent. Without it, present the battlecard as formatted chat text instead.\n\nWhen writing Python for Code Interpreter, avoid building scripts through shell heredocs or string concatenation that has to escape the battlecard data inline, especially once brand colors, hex codes, or parenthesized function calls like `RGBColor(...)` are involved, unbalanced quotes or parens in the data will break the heredoc before your code ever runs. Write the JSON data to its own file (`json.dump`) and have the script `json.load` it, or write the whole script as a single Python file directly rather than assembling it through shell quoting.\n\n## Step 1: Gather the inputs\n\n1. **Which kind of comparison this is** — ask first, before anything else: is this a sales battlecard advocating for one specific product against competitors, or a neutral, unbiased comparison with no side taken? This determines the entire shape of what you build next, not just the wording:\n   - **Battlecard**: one product is \"ours,\" the rest are competitors, compared pairwise (us vs. each one). Objection handling applies (Step 3's `objections` array), since that's inherently a sales concept, what a prospect pushes back with and how to respond.\n   - **Neutral comparison**: there is no \"ours.\" Every item is a peer, laid out as one shared matrix, all items as columns, one set of dimensions as rows, no anchor item privileged over the others. Never use the word \"competitor\" anywhere in this mode, not in what you say to the person, not in the data. Skip objection handling entirely, there's no seller position to defend. Gather comparably balanced detail on every item.\n2. **The items being compared** — for a battlecard: your product\u002Fcompany (name and a one-line positioning statement) plus one or more competitors. For a neutral comparison: two or more named items, genuinely peers, no ordering implication.\n3. **What the person already knows** — pitch, strengths, weaknesses for each item, and (battlecard only) objections prospects commonly raise. This is the primary source of truth. Ask for it directly rather than assuming you already know the landscape.\n4. **Permission to supplement with web research** (optional) — ask whether they'd like you to search for additional public information (pricing pages, marketing claims, review sites) to round out gaps. If they decline, work only from what they gave you.\n\nOptionally ask for **category tags** (e.g. Pricing, Security, Integration, Support, Ecosystem) if they want the comparison filterable by category as well as by item. If they don't have a preference, pick 3-5 sensible categories based on the content itself.\n\n5. **Output format** — ask directly: an interactive HTML app (filterable, searchable, with a live scorecard tally, opens in any browser), or a PowerPoint deck (easy to present or drop into an existing deck)? Build only the one they ask for. Don't generate both by default, and don't guess, this is a real fork in what Step 4 does next.\n6. **Branding** — ask whether they have a PowerPoint branding template or brand guidelines (colors, fonts, logo) this should follow. If yes, ask for either the actual `.pptx` template file, or at minimum the core details: primary\u002Fsecondary colors (hex if they have them), font names, and a logo if relevant. If no, say plainly that you'll use a clean, generic default, and move on, this isn't worth belaboring if they don't have one.\n\n## Step 2: The credibility rule (read this before writing anything)\n\nA battlecard a sales rep can't trust is worse than no battlecard, it gets someone caught flat-footed in front of a customer. Every single claim in this skill's output carries a `source` (`\"user\"` or `\"web\"`) and a `verified` flag:\n\n- Anything the person told you directly → `source: \"user\"`, `verified: true`.\n- Anything you found through web research → `source: \"web\"`, `verified: false` until the person explicitly confirms it.\n\n**Never invent a competitor claim.** If you don't have it from the person or a real search result, leave it out rather than filling the gap with something plausible-sounding.\n\nBefore generating the final deliverable, if there are any `web`-sourced claims, show the person exactly what you found, don't summarize or paraphrase it away. For each one, list which competitor it's about, which section it would go in (pitch, win, vulnerability, or objection), and the claim text itself, close to verbatim from the source. End with something low-friction like \"Let me know if anything's off and I'll fix it before it goes into the [html app \u002F deck].\" A person should be able to skim the list and immediately spot anything wrong, not have to reconstruct what you actually found from a vague summary.\n\nOnly after that review does a claim get treated as trustworthy in the shipped battlecard. Claims that remain unconfirmed (the person didn't respond to that specific one, or explicitly said \"not sure\") should either be dropped or clearly marked `unverified` in the output, never silently presented as settled fact. If they flag something as wrong, correct or remove it and don't carry it into Step 3 as-is.\n\n## Step 3: Build the data model\n\nThe two modes need genuinely different data shapes, not just different labels on the same one. Battlecard mode is inherently pairwise, \"us\" versus each competitor, one at a time. Neutral mode is not: there's no anchor item, every item being compared is a peer, so the data model is a real matrix, one shared set of dimensions with a value for every item, not a series of us-vs-X comparisons.\n\n### If `mode` is `\"battlecard\"`\n\n```json\n{\n  \"mode\": \"battlecard\",\n  \"us\": { \"name\": \"string\", \"tagline\": \"string\" },\n  \"categories\": [\"Pricing\", \"Security\", \"...\"],\n  \"competitors\": [\n    {\n      \"id\": \"slug\",\n      \"name\": \"Display name\",\n      \"theirPitch\": { \"text\": \"string\", \"source\": \"user|web\", \"verified\": true },\n      \"comparison\": [\n        {\n          \"category\": \"one of categories[]\",\n          \"dimension\": \"Short label for what's being compared, e.g. 'Recording capture method'\",\n          \"us\": { \"text\": \"Our position on this dimension\", \"source\": \"user|web\", \"verified\": true },\n          \"them\": { \"text\": \"Their position on this dimension\", \"source\": \"user|web\", \"verified\": true },\n          \"edge\": \"us|them|even\"\n        }\n      ],\n      \"objections\": [\n        { \"objection\": \"What the prospect says\", \"response\": \"How to handle it\", \"source\": \"user|web\", \"verified\": true }\n      ]\n    }\n  ]\n}\n```\n\n### If `mode` is `\"neutral\"`\n\n```json\n{\n  \"mode\": \"neutral\",\n  \"items\": [\n    { \"id\": \"slug\", \"name\": \"Display name\", \"positioning\": { \"text\": \"string\", \"source\": \"user|web\", \"verified\": true } }\n  ],\n  \"categories\": [\"Pricing\", \"Security\", \"...\"],\n  \"matrix\": [\n    {\n      \"category\": \"one of categories[]\",\n      \"dimension\": \"Short label for what's being compared\",\n      \"values\": {\n        \"item-id-1\": { \"text\": \"That item's position on this dimension\", \"source\": \"user|web\", \"verified\": true },\n        \"item-id-2\": { \"text\": \"That item's position on this dimension\", \"source\": \"user|web\", \"verified\": true }\n      },\n      \"best\": \"item-id-of-whichever-item-leads-this-dimension, or null if genuinely even\"\n    }\n  ]\n}\n```\n\n`items` needs at least two entries, `values` on every `matrix` row should have an entry for every item in `items` (missing ones render as blank\u002Funverified rather than breaking anything, but that means incomplete data, go back and fill the gap). Never use the word \"competitor\" anywhere in neutral-mode language, in what you say to the person or in the data itself, there isn't one, that's the whole point of this mode.\n\n### Applies to both modes\n\n- **Set `mode` from what they told you in Step 1.** This isn't cosmetic, it changes both the data shape above and how the app renders and labels everything.\n- **Be honest about who leads a dimension** (`edge` in battlecard mode, `best` in neutral mode). Not every dimension has to favor the same side, and \"even\" \u002F `null` is a legitimate, credible answer, not a cop-out. A card where every single row favors the same item reads as marketing, not something anyone can trust.\n- **`category` must match one of the top-level `categories[]` values exactly**, the filtering keys off this.\n- **Order `comparison`\u002F`matrix` rows grouped by category**, all rows for one category together before moving to the next. The app inserts a category header whenever the category changes from the row before it, not once per unique category, so an interleaved order (Pricing, Security, Pricing) produces a duplicate \"Pricing\" header instead of one clean group.\n- Keep every `text`\u002F`response` field a plain sentence a rep could say out loud, not a bullet fragment.\n- `id` values must be unique, stable slugs (used for filtering and DOM references, not shown to the user).\n- Aim for real coverage: if the person only gave you two comparison points, ask for a few more before calling it done. A one-row \"comparison\" isn't a battlecard.\n\n## Step 4a: If they chose HTML\n\nUsing Code Interpreter:\n\n1. Read `assets\u002Fbattlecard-app-template.html`.\n2. **If the person gave you brand colors**, update the CSS custom properties near the top of the `\u003Cstyle>` block (`--blue`, `--green`, `--red`, `--amber`, and their `-light` companions) to match. In battlecard mode, `--green`\u002F`--red` drive the win\u002Floss highlighting, keep those two visually distinct from each other regardless of the brand palette, the highlighting only works if someone can tell the colors apart at a glance. In neutral mode the matrix only ever uses `--blue` for its single neutral highlight, there's no red\u002Fgreen to worry about there. If they gave you a logo, you can add it next to the title, but don't restructure the layout to accommodate it. If they didn't give you branding, leave the defaults as-is, don't invent a color scheme.\n3. Serialize the JSON from Step 3 and escape it for safe embedding: replace every `\u003C\u002F` with `\u003C\\\u002F` before inserting it. This prevents an item name or claim containing the literal text `\u003C\u002Fscript` from prematurely closing the embedded `\u003Cscript>` tag.\n   ```python\n   import json\n   json_str = json.dumps(battlecard_data).replace(\"\u003C\u002F\", \"\u003C\\\\\u002F\")\n   ```\n4. Replace the placeholder token `{{BATTLECARD_DATA_JSON}}` in the template with the escaped string.\n5. Save and return the completed file as a downloadable `.html`, named after what's being compared (e.g. `battlecard-vs-competitor-name.html` for battlecard mode, `comparison-item-a-vs-item-b.html` for neutral mode).\n\nDo this through Code Interpreter so it comes back as an actual file with a download link, printing raw HTML into the chat as text won't render it, the platform will just show the literal markup. If Code Interpreter isn't available, present the same content as formatted chat text instead: battlecard mode grouped by competitor with us\u002Fthem pairs per dimension plus objection handling; neutral mode as a single table with one row per dimension and one column per item.\n\nIf the person later wants a PowerPoint version too, that's a separate follow-up ask, go to Step 4b at that point using the same underlying data.\n\n## Step 4b: If they chose PowerPoint\n\n**If the person gave you a `.pptx` branding template**, open it as the base presentation (`Presentation(\"their_template.pptx\")` in `python-pptx`) and add your slides using their existing slide masters and layouts, so the deck inherits their fonts, colors, and placeholder positions rather than starting from a blank default. Only fall back to building a plain default presentation if they didn't provide one.\n\n**If they only gave you colors\u002Ffonts (no template file)**, apply those to a default layout: use their font for headings and body text, and use their primary\u002Fsecondary colors for the title slide and the \"who has the edge\" table highlighting, keeping the win\u002Floss colors visually distinct from each other regardless of the palette.\n\n**A `python-pptx` gotcha worth knowing before you hit it**: `RGBColor` is a tuple subclass, it has no `.red`\u002F`.green`\u002F`.blue` attributes, indexing into it (`color[0]`, `color[1]`, `color[2]`) works but attribute access throws. If you need a lighter tint of a brand color for cell shading (the same idea as the HTML app's `-light` color variants), do the math on plain integers before constructing the `RGBColor`, not after:\n```python\ndef lighten(hex_color, amount=0.85):\n    hex_color = hex_color.lstrip('#')\n    r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)\n    r = int(r + (255 - r) * amount)\n    g = int(g + (255 - g) * amount)\n    b = int(b + (255 - b) * amount)\n    return RGBColor(r, g, b)\n```\n\n**If they gave you neither**, use a clean, professional default, don't invent a brand identity, just keep it legible and uncluttered.\n\n**Don't ship a bare `python-pptx` table dump.** Left unstyled, `python-pptx` tables render with PowerPoint's default banded-blue table style, small unstyled text, and no visual hierarchy, that reads as an obvious AI-generated afterthought, not something a rep would actually present from. Override the defaults explicitly:\n\n- **Header row**: solid fill (brand primary color, or a dark navy\u002Fslate if no branding was given), white or light text, bold, 12-13pt.\n- **Body cells**: at least 12pt, don't leave `python-pptx`'s tiny default size.\n- **Category-group rows** need their own distinct fill (a light tint of the brand color, or a neutral gray), not plain text at the same visual weight as the data rows below them, otherwise they don't read as section breaks at all.\n- **The title slide needs actual visual weight**: a large bold title (32pt+) and a filled background shape or color block behind it, not plain black text centered on a blank white slide.\n- **Never use a thin accent stripe or colored bar as the only decoration** (a header\u002Ffooter bar spanning the slide, a vertical sidebar stripe, a single-edge border on a box), that's a hallmark of low-effort AI-generated slides. If a card or section needs visual separation, use a filled shape or a solid background tint instead, not a thin line.\n- Keep consistent margins (0.5\"+ from every slide edge) and consistent spacing between elements, don't let gaps vary randomly from slide to slide.\n- Check cell text length against column width before finalizing. If text would wrap past 3-4 lines in a cell, that's a sign to split the competitor across two slides (see the density note below), not to keep shrinking the font until it's unreadable.\n\nIf your sandbox can render the deck to an image before finalizing (for example, converting to PDF and rasterizing a page), do a quick visual pass for text overflow, overlap, or low-contrast text against its background before calling it done. If that's not available in your environment, apply the sizing and wrapping guidance above defensively, since you won't get a check on the actual output.\n\nStructure it depends on which mode you're in, since the data shapes are genuinely different (see Step 3):\n\n**In `\"battlecard\"` mode**: a title slide (your name vs the competitor(s)), then for each competitor, a comparison table with three columns (Dimension \u002F Us \u002F Them), one row per `comparison` entry, the `edge` winner's cell given a background fill, green tint for `\"us\"`, red tint for `\"them\"`, matching the HTML app's convention. Follow each competitor's table with a short objection-handling section (still text, that one doesn't fit a table format).\n\n**In `\"neutral\"` mode**: a title slide, then one single matrix table, not a table per item. Columns are Dimension plus one column per entry in `items[]`, in the same order every time. Rows come from `matrix[]`, one row per dimension, and the `best` item's cell (if any) gets a single neutral accent fill, never red\u002Fgreen, since that would imply a bias that isn't there. If `best` is `null` for a row, no cell gets highlighted. There's no objection-handling section in this mode, there's no `objections` data to draw from.\n\nIn either mode, group rows by category if there are more than 5-6 comparison points, so it doesn't read as a wall of rows. If the matrix or a competitor's table has enough entries that one table would be unreadably dense (this gets tighter fast in neutral mode as `items[]` grows, a 5-column matrix eats width quickly), split by category across multiple slides rather than shrinking text to fit or cramming columns narrower than they can hold their content.\n\n**Carry the verification status into the deck.** Any cell built from a claim with `verified: false` needs a visible marker in the cell text itself (append \"(unverified)\"), whether that's a `us`\u002F`them` cell in battlecard mode or an item's cell in the neutral matrix. Don't drop the distinction just because it's no longer in an app with a badge, someone presenting from a printed or shared deck needs the same warning the HTML app gives them.\n\nIf the person later asks for a rescoped version (\"just give me a deck for these two\" or \"only the pricing category\"), regenerate a fresh `.pptx` from the same underlying data filtered to their request rather than trying to edit the existing file.\n\nIf the person later wants the HTML version too, that's a separate follow-up ask, go to Step 4a at that point using the same underlying data.\n\n## A few things to keep in mind\n\n- If someone wants to add an item (a competitor in battlecard mode, another peer in neutral mode) or update intel on an existing comparison, don't rebuild from scratch, extend the existing JSON data and regenerate whichever output format(s) they're using. Run any new claims through the Step 2 verification gate the same as the first time.\n- If the person names an item you have no information about and declines web research, say so plainly and ask them to supply at least the basics (their positioning, and a couple of real comparison points) rather than fabricating a plausible-sounding profile.\n- If they hand you branding partway through an existing session, regenerate the output with it applied rather than trying to patch colors into an already-delivered file.\n- Keep tone factual throughout. In battlecard mode, this is a tool someone reads on a call, not a marketing piece. In neutral mode, this doubles: no marketing tone, and no accidental advocacy for whichever item happened to get gathered first.\n",{"name":5,"description":7},"main","3678d23ad8fb8d098c428302a98044ac9514d5a1","submissions\u002Fcompetitive-battlecard-builder",120,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-13 01:39:10",{"source":8,"skillName":5,"commands":35,"prompt":45,"zipAvailable":46},[36,39,42],{"tool":37,"command":38},"npx","npx skills add microsoft\u002Fcat-agent-skills --skill competitive-battlecard-builder",{"tool":40,"command":41},"bunx","bunx skills add microsoft\u002Fcat-agent-skills --skill competitive-battlecard-builder",{"tool":43,"command":44},"pnpm","pnpm dlx skills add microsoft\u002Fcat-agent-skills --skill competitive-battlecard-builder","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fcompetitive-battlecard-builder\u002FSKILL.md",true,[48,54,60],{"path":49,"resourceType":50,"resourceTypeDesc":51,"sizeBytes":52,"sourceHtmlUrl":53},"assets\u002Fbattlecard-app-template.html","ASSET","资产文件",34574,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fcompetitive-battlecard-builder\u002Fassets\u002Fbattlecard-app-template.html",{"path":55,"resourceType":56,"resourceTypeDesc":57,"sizeBytes":58,"sourceHtmlUrl":59},"metadata.json","REFERENCE","参考文档",752,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fcompetitive-battlecard-builder\u002Fmetadata.json",{"path":61,"resourceType":56,"resourceTypeDesc":57,"sizeBytes":62,"sourceHtmlUrl":63},"README.md",2641,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fcompetitive-battlecard-builder\u002FREADME.md",[65,92,117,155,173,189],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":20},"01M6X3P09R8SJH3QJGMTAA5N90","stakeholder-and-decision-map","Mines customer interactions to identify stakeholders, infer their likely roles and engagement, map the decision process, and highlight who or what may be missing. Use when asked to \"map a deal's buying group\", identify influential or missing stakeholders, \"assess who has gone quiet\", or \"explain how the customer will decide\". Do not use for phased engagement plans, contact sequences, choosing or drafting outreach to the single next contact, seller or team performance reviews, or contacting, scheduling, or changing records.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fstakeholder-and-decision-map\u002FSKILL.md",[],[72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91],"decision","map","decision map","stakeholder and decision map","performance","customer","team","how","decide","do","next","infer","missing","records","assess","reviews","for","single","when","sequences",{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":20},"01M6X3NRES2XSZQCH015WD4JN6","sales-progress-update","Creates or refreshes a sales progress update for a manager or team about work done, deal movement, pending outcomes, supported forecast position, and help needed. Use when asked to \"create my weekly sales update\", \"refresh my sales progress deck\", \"report my forecast position\", \"where do I stand against my quarter forecast\", or \"catch my manager up on committed deals\". Do NOT use for non-sales project updates, a since-date deal change digest, momentum or blocker analysis, meeting prep, or outreach drafting.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Fsales-progress-update\u002FSKILL.md",[],[99,100,101,102,103,104,105,106,78,107,108,109,110,111,112,113,114,115,116,90],"update","sales","progress","sales progress update","change","create","meeting prep","analysis","work","up","done","digest","project","my","updates","manager","weekly","catch",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":136,"publishedAt":20},"01M6X3MSDTB1E5H6DQAFAY41YP","submission-assemble","Put the checked CSR or 510(k) sections together in template order with a reviewer-facing open-items register, as a draft package for human sign-off. Use when the user asks \"assemble the CSR draft\", \"put the module together for review\", \"build the review package with the open items on top\", \"compile the 510(k) sections in order\" or \"package this up for the submission lead\". Do NOT use for assembling an unchecked draft - use consistency-check instead. Do NOT use for loading protocol, SAP or TLF sources - use source-ingest instead. Do NOT use for compiling general documents or slide decks - use the built-in docx or pptx skill instead. Do NOT use for filing or submitting to a regulator - use the sponsor's validated gateway, operated by a human, instead. Guardrail: draft for human approval only (L1); status is draft_clean or draft_open_items, never submitted.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Freg-submission-authoring\u002Fsubmission-assemble\u002FSKILL.md",[123,128,131],{"categoryNo":124,"categoryName":125,"taxonomyType":126,"taxonomyTypeDesc":127},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",{"categoryNo":129,"categoryName":130,"taxonomyType":126,"taxonomyTypeDesc":127},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":132,"categoryName":133,"taxonomyType":134,"taxonomyTypeDesc":135},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[137,138,139,140,141,142,143,144,145,146,147,148,108,149,150,151,152,153,154,90],"assemble","submission","submission assemble","docx","pptx","writing","documents","healthcare","open","reviewer","never","only","clean","guardrail","slide decks","sources","filing","loading",{"skillNo":156,"name":157,"nameZh":6,"description":158,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":159,"categories":160,"keywords":161,"publishedAt":20},"01M6X3MM9TVWDQ716QGNV4DS25","source-ingest","Pull every number, date and population definition out of a study's protocol, SAP, TLF tables, investigator brochure and prior approved text into one cited fact map, keyed per config\u002Ffield-dictionary.json, before any CSR or 510(k) section is written. Use when the user asks \"here's the locked TLF set and SAP, set up the CSR\", \"load the protocol and tables before we start drafting\", \"what figures do we have from the database lock?\", \"pull the disposition counts out of Table 14.1.1\" or \"ingest the IB for the 510(k)\". Do NOT use for drafting or assembling submission sections - use section-draft or submission-assemble instead. Do NOT use for reconciling a patient's medications - use the discharge-care-transition plugin instead. Guardrail: reads and cites sources only; never estimates, rounds or invents a fact.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Freg-submission-authoring\u002Fsource-ingest\u002FSKILL.md",[],[162,163,164,165,166,106,144,167,147,148,108,73,168,150,152,169,170,171,172,90],"source ingest","ingest","source","database","SET","text","patient","number","lock","definition","transition",{"skillNo":174,"name":175,"nameZh":6,"description":176,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":180,"publishedAt":20},"01M6X3M7ZH5ZNEHP56BQ74HAJR","section-plan","Work out which sections a CSR or 510(k) requires, from the templates in config, check each one against the cited fact map, and name what is missing before a word is drafted. Use when the user asks \"what do we still need before we can draft the CSR?\", \"which sections are ready to start?\", \"what's blocking the performance data section?\", \"do we have everything for the synopsis?\" or \"map our tables to the E3 structure\". Do NOT use for writing section language - use section-draft instead. Do NOT use for cross-checking a finished draft - use consistency-check instead. Do NOT use for outlining general Word documents or business reports - use the built-in docx skill instead. Only structures with a template in config are supported. Guardrail: plans only; never marks a section ready on a missing fact and never invents a structure.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Freg-submission-authoring\u002Fsection-plan\u002FSKILL.md",[179],{"categoryNo":124,"categoryName":125,"taxonomyType":126,"taxonomyTypeDesc":127},[181,182,183,140,142,76,143,106,144,107,147,148,184,185,186,73,150,187,188,90],"plan","section","section plan","ready","reports","structure","word documents","which",{"skillNo":190,"name":191,"nameZh":6,"description":192,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":193,"categories":194,"keywords":195,"publishedAt":20},"01M6X3M0V1MJNCR653ES9XGVMS","section-draft","Write or revise the language for one planned CSR or 510(k) section - synopsis, efficacy, safety, discussion, device description - using only figures already in the cited fact map, each with its TLF or SAP citation. Use when the user asks \"draft the Synopsis from our locked tables\", \"write up safety from Table 14.3.1\", \"give me a first pass at the discussion\", \"draft the device description for the 510(k)\" or \"tighten the wording of the Discussion section\". Do NOT use for supplying a number not already in source_facts - use source-ingest instead. Do NOT use for verifying a draft's figures - use consistency-check instead. Do NOT use for appeal letters, clinical notes or discharge summaries - use the matching HLS plugin instead. Guardrail: drafts for human approval only (L1); never asserts an unsourced number or harmonises two figures.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcat-agent-skills\u002Fblob\u002Fmain\u002Fsubmissions\u002Freg-submission-authoring\u002Fsection-draft\u002FSKILL.md",[],[196,182,197,142,198,144,199,147,148,108,200,73,201,202,203,150,204,169,205,90],"draft","section draft","notes","write","me","human","life","clinical","drafts","at"]