[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-anthropic-tres-import-contacts":3,"mdc--r1gxt6-key":34,"related-repo-anthropic-tres-import-contacts":1333,"related-org-anthropic-tres-import-contacts":1435},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":23,"repoUrl":24,"updatedAt":25,"license":26,"forks":27,"topics":28,"repo":29,"sourceUrl":32,"mdContent":33},"tres-import-contacts","import contacts into TRES Finance","Import contacts (address book entries) into TRES Finance from a CSV or XLSX file. Use this skill whenever the user wants to import, upload, or bulk-add contacts, address labels, or address book entries into TRES — whether from a file they filled in after using the export-3rd-party-contacts skill, or from any CSV\u002FXLSX that maps addresses to names. Trigger phrases include: 'import contacts', 'upload contacts', 'add contacts from file', 'import address book', 'load contacts into TRES', 'bulk label addresses', 'import the contacts file', 'upload the filled contacts sheet'. Also trigger when the user says something like 'I filled in the contacts file, now import it' or 'here is my contacts spreadsheet, please upload it to TRES'. Do NOT trigger for exporting or extracting unidentified addresses — that's the export-3rd-party-contacts skill. Do NOT trigger for viewing or searching the existing address book.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"anthropic","Anthropic","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fanthropic.png","anthropics",[13,17,20],{"name":14,"slug":15,"type":16},"Operations","operations","tag",{"name":18,"slug":19,"type":16},"Finance","finance",{"name":21,"slug":22,"type":16},"Data Cleaning","data-cleaning",294,"https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fclaude-plugins-community","2026-07-02T07:37:39.234025",null,69,[],{"repoUrl":24,"stars":23,"forks":27,"topics":30,"description":31},[],"Community plugin marketplace for Claude Cowork and Claude Code. Read-only mirror — submit plugins at clau.de\u002Fplugin-directory-submission.","https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fclaude-plugins-community\u002Ftree\u002FHEAD\u002Ftres-finance-plugin\u002Fskills\u002Ftres-import-contacts","---\nname: tres-import-contacts\ndescription: \"Import contacts (address book entries) into TRES Finance from a CSV or XLSX file. Use this skill whenever the user wants to import, upload, or bulk-add contacts, address labels, or address book entries into TRES — whether from a file they filled in after using the export-3rd-party-contacts skill, or from any CSV\u002FXLSX that maps addresses to names. Trigger phrases include: 'import contacts', 'upload contacts', 'add contacts from file', 'import address book', 'load contacts into TRES', 'bulk label addresses', 'import the contacts file', 'upload the filled contacts sheet'. Also trigger when the user says something like 'I filled in the contacts file, now import it' or 'here is my contacts spreadsheet, please upload it to TRES'. Do NOT trigger for exporting or extracting unidentified addresses — that's the export-3rd-party-contacts skill. Do NOT trigger for viewing or searching the existing address book.\"\n---\n\n# TRES Finance — Import Contacts from CSV\u002FXLSX\n\n## Goal\n\nRead a user-provided CSV or XLSX file containing address-to-name mappings and import them as contacts into the TRES address book. This is the second half of the contacts workflow: the user previously exported unidentified addresses (via the export skill), filled in names and tags, and now wants to push those labels back into TRES.\n\n## MCP Server\n\nAll GraphQL calls use the **user-tres-finance** MCP server (`execute` tool).\n\n## Expected File Format\n\nThe file should have these columns (header names are matched case-insensitively):\n\n| Contact Name | Contact Address | Contact Tag |\n|---|---|---|\n| Binance Hot Wallet | 0xABC... | Exchange |\n| Vendor X | 0xDEF... | Vendor |\n\n- **Contact Name** — the label to assign to the address (required for import; rows without a name are skipped)\n- **Contact Address** — the blockchain address (required)\n- **Contact Tag** — optional tag(s) for the contact. Multiple tags can be separated by commas (e.g. `Exchange, Custodian`)\n\nThe column order doesn't matter — the skill matches by header name. The file may contain additional columns (like the \"Address details\" enrichment columns from the export skill) — those are ignored.\n\n## Workflow\n\n### Step 1 — Authenticate\n\nCall `get_viewer` (no arguments) to confirm the session is active and note the organization name.\n\n### Step 2 — Read and parse the file\n\nThe user will provide a CSV or XLSX file. Detect the format from the file extension.\n\n**For XLSX files:**\nUse Python with `openpyxl==3.1.5`. If not installed, stop and display:\n> \"openpyxl is not installed. Please run: `python3 -m venv .venv && .venv\u002Fbin\u002Fpip install openpyxl==3.1.5`\"\n\n- If the workbook has multiple sheets, look for one named \"Contacts\" (case-insensitive). If not found, use the first sheet.\n- Read the header row to find the column indices for \"Contact Name\", \"Contact Address\", and \"Contact Tag\" (match case-insensitively, trim whitespace).\n- Read all data rows.\n- Filter out trailing empty rows — openpyxl sometimes reads extra `None` rows at the bottom of the sheet. Skip any row where all cells are `None` or empty strings.\n\n**For CSV files:**\nUse Python's built-in `csv` module.\n\n- Open the file with `encoding='utf-8-sig'` to handle the BOM (byte-order mark) that Excel adds when saving CSVs. Without this, the first header may appear as `\\ufeffContact Name` and won't match.\n- Try comma delimiter first. If the header row doesn't contain the expected columns, retry with semicolon delimiter (common in European-locale Excel exports).\n- Read the header row to find column indices, same matching logic as above.\n- Read all data rows.\n\n### Step 3 — Validate and filter\n\nFor each row:\n\n1. **Skip if Contact Name is blank or missing** — the user hasn't identified this address yet, so there's nothing to import.\n2. **Skip if Contact Address is blank or missing** — can't label an address that doesn't exist.\n3. **Trim whitespace** from name, address, and tag values.\n4. **Parse tags**: if Contact Tag contains commas, split into multiple tags. Trim each tag. Remove empty strings.\n5. **Deduplicate by address** (case-insensitive): if the same address appears multiple times, keep the last occurrence (the user likely corrected it). Warn about duplicates.\n\nAfter filtering, report to the user:\n- Total rows in the file\n- Rows skipped (blank name)\n- Rows to import\n- Any duplicates found\n\nThen show a preview table of the first 10 rows that will be imported (address, name, tags) and ask the user to confirm before proceeding.\n\n### Step 4 — Check existing contacts (only for imports with 50+ rows)\n\nFor small imports (\u003C50 rows), skip this step and go straight to importing — fetching the entire address book for a handful of contacts adds unnecessary delay.\n\nFor larger imports, fetch the current address book to identify which addresses already have labels. This helps the user understand what will be created vs. updated.\n\n```graphql\nquery AddressBook($limit: Int, $offset: Int) {\n  customAccountNameLabel(limit: $limit, offset: $offset) {\n    results {\n      originalIdentifier\n      labelValue\n      tags\n    }\n    totalCount\n  }\n}\n```\n\nPaginate through all results (500 at a time). Build a lookup dictionary keyed by `originalIdentifier.lower()`.\n\nCompare against the import list and report:\n- **New contacts** — addresses not in the current address book\n- **Updates** — addresses that already exist but will get a new name or tags\n\n**Important**: `setCustomAccountNameLabelTags` is a **replace** operation, not append. If a contact currently has tags `[\"Exchange\", \"Custodian\"]` and the import file has `[\"Vendor\"]`, the old tags will be overwritten. Warn the user about this for any contacts being updated that already have tags.\n\nPresent this breakdown to the user before proceeding. If there are updates, list a few examples showing old name → new name so the user can sanity-check.\n\n### Step 5 — Import contacts\n\nFor each validated row, make two API calls:\n\n**5a. Set the contact name:**\n\n```graphql\nmutation SetContactName($identifier: String, $labelValue: String) {\n  setCustomAccountName(identifier: $identifier, labelValue: $labelValue) {\n    accountTxsSummary {\n      accountIdentifier\n      displayName\n    }\n  }\n}\n```\n\nVariables:\n```json\n{\n  \"identifier\": \"\u003Cthe address>\",\n  \"labelValue\": \"\u003Cthe contact name>\"\n}\n```\n\n**5b. Set tags (only if the row has tags):**\n\n```graphql\nmutation SetContactTags($identifier: String!, $tags: [String]!) {\n  setCustomAccountNameLabelTags(identifier: $identifier, tags: $tags) {\n    accountTxsSummary {\n      accountIdentifier\n      displayName\n    }\n  }\n}\n```\n\nVariables:\n```json\n{\n  \"identifier\": \"\u003Cthe address>\",\n  \"tags\": [\"Exchange\", \"Custodian\"]\n}\n```\n\n**Batching strategy:** The API doesn't have a bulk endpoint. For small imports (\u003C20 contacts), fire all name mutations in parallel, then all tag mutations in parallel — this is fast and safe. For larger imports, process in batches of 10 parallel calls to avoid overwhelming the API, and show progress to the user every 25 contacts (e.g. \"Imported 25\u002F142...\").\n\n**Error handling:** If a mutation fails for a specific address, log the error and continue with the next row. Don't stop the entire import. Collect all failures for the summary.\n\n### Step 6 — Summary\n\nAfter all mutations complete, present a summary:\n\n- Total contacts imported successfully (name set)\n- Total contacts with tags set\n- Any failures (list the address and error message)\n\nIf there were failures, suggest the user can retry by running the skill again with the same file — already-imported contacts will just be updated (the mutation is idempotent for names).\n\n## Edge Cases\n\n- **File has no header row**: If the first row doesn't contain recognizable column names, tell the user the expected format and ask them to fix the file.\n- **File has wrong columns**: If \"Contact Address\" column can't be found, the file isn't usable. Report which columns were found and what's expected.\n- **Empty file**: If the file has headers but no data rows, tell the user.\n- **All names blank**: If every row has a blank Contact Name, tell the user — they probably uploaded the unfilled template by mistake.\n- **Very large files (>500 rows)**: Warn the user this will take a while and ask for confirmation.\n- **Special characters in names**: Pass through as-is — TRES handles unicode in labels.\n- **Mixed case addresses**: The `setCustomAccountName` mutation accepts addresses as-is. Don't normalize case — pass the original value from the file.\n- **Tag with extra spaces**: `\" Exchange , Custodian \"` → trim to `[\"Exchange\", \"Custodian\"]`.\n- **Address not in any transaction**: The mutations work even for addresses that TRES has never seen in a transaction — they create the address book entry regardless.\n- **Tags overwrite**: `setCustomAccountNameLabelTags` replaces existing tags entirely. If the user only wants to add tags, they would need to merge with existing tags first (the skill does not do this automatically — it's a replace operation). Flag this to the user when updating existing contacts that already have tags.\n- **CSV BOM prefix**: Excel-saved CSVs include a UTF-8 BOM (`\\ufeff`) that corrupts the first column header. Always open CSVs with `encoding='utf-8-sig'`.\n- **Semicolon-delimited CSVs**: European Excel exports use `;` instead of `,`. If comma parsing doesn't find the expected headers, retry with semicolon.\n- **Trailing empty rows in XLSX**: openpyxl reads empty rows at the end of the sheet. Skip rows where all values are `None` or empty.\n",{"data":35,"body":36},{"name":4,"description":6},{"type":37,"children":38},"root",[39,48,55,61,67,89,95,100,169,209,214,220,227,240,246,251,269,286,324,342,380,386,391,445,450,473,478,484,489,494,597,610,615,638,679,684,690,695,703,771,776,878,886,951,955,1071,1081,1091,1097,1102,1120,1125,1131,1327],{"type":40,"tag":41,"props":42,"children":44},"element","h1",{"id":43},"tres-finance-import-contacts-from-csvxlsx",[45],{"type":46,"value":47},"text","TRES Finance — Import Contacts from CSV\u002FXLSX",{"type":40,"tag":49,"props":50,"children":52},"h2",{"id":51},"goal",[53],{"type":46,"value":54},"Goal",{"type":40,"tag":56,"props":57,"children":58},"p",{},[59],{"type":46,"value":60},"Read a user-provided CSV or XLSX file containing address-to-name mappings and import them as contacts into the TRES address book. This is the second half of the contacts workflow: the user previously exported unidentified addresses (via the export skill), filled in names and tags, and now wants to push those labels back into TRES.",{"type":40,"tag":49,"props":62,"children":64},{"id":63},"mcp-server",[65],{"type":46,"value":66},"MCP Server",{"type":40,"tag":56,"props":68,"children":69},{},[70,72,78,80,87],{"type":46,"value":71},"All GraphQL calls use the ",{"type":40,"tag":73,"props":74,"children":75},"strong",{},[76],{"type":46,"value":77},"user-tres-finance",{"type":46,"value":79}," MCP server (",{"type":40,"tag":81,"props":82,"children":84},"code",{"className":83},[],[85],{"type":46,"value":86},"execute",{"type":46,"value":88}," tool).",{"type":40,"tag":49,"props":90,"children":92},{"id":91},"expected-file-format",[93],{"type":46,"value":94},"Expected File Format",{"type":40,"tag":56,"props":96,"children":97},{},[98],{"type":46,"value":99},"The file should have these columns (header names are matched case-insensitively):",{"type":40,"tag":101,"props":102,"children":103},"table",{},[104,128],{"type":40,"tag":105,"props":106,"children":107},"thead",{},[108],{"type":40,"tag":109,"props":110,"children":111},"tr",{},[112,118,123],{"type":40,"tag":113,"props":114,"children":115},"th",{},[116],{"type":46,"value":117},"Contact Name",{"type":40,"tag":113,"props":119,"children":120},{},[121],{"type":46,"value":122},"Contact Address",{"type":40,"tag":113,"props":124,"children":125},{},[126],{"type":46,"value":127},"Contact Tag",{"type":40,"tag":129,"props":130,"children":131},"tbody",{},[132,151],{"type":40,"tag":109,"props":133,"children":134},{},[135,141,146],{"type":40,"tag":136,"props":137,"children":138},"td",{},[139],{"type":46,"value":140},"Binance Hot Wallet",{"type":40,"tag":136,"props":142,"children":143},{},[144],{"type":46,"value":145},"0xABC...",{"type":40,"tag":136,"props":147,"children":148},{},[149],{"type":46,"value":150},"Exchange",{"type":40,"tag":109,"props":152,"children":153},{},[154,159,164],{"type":40,"tag":136,"props":155,"children":156},{},[157],{"type":46,"value":158},"Vendor X",{"type":40,"tag":136,"props":160,"children":161},{},[162],{"type":46,"value":163},"0xDEF...",{"type":40,"tag":136,"props":165,"children":166},{},[167],{"type":46,"value":168},"Vendor",{"type":40,"tag":170,"props":171,"children":172},"ul",{},[173,183,192],{"type":40,"tag":174,"props":175,"children":176},"li",{},[177,181],{"type":40,"tag":73,"props":178,"children":179},{},[180],{"type":46,"value":117},{"type":46,"value":182}," — the label to assign to the address (required for import; rows without a name are skipped)",{"type":40,"tag":174,"props":184,"children":185},{},[186,190],{"type":40,"tag":73,"props":187,"children":188},{},[189],{"type":46,"value":122},{"type":46,"value":191}," — the blockchain address (required)",{"type":40,"tag":174,"props":193,"children":194},{},[195,199,201,207],{"type":40,"tag":73,"props":196,"children":197},{},[198],{"type":46,"value":127},{"type":46,"value":200}," — optional tag(s) for the contact. Multiple tags can be separated by commas (e.g. ",{"type":40,"tag":81,"props":202,"children":204},{"className":203},[],[205],{"type":46,"value":206},"Exchange, Custodian",{"type":46,"value":208},")",{"type":40,"tag":56,"props":210,"children":211},{},[212],{"type":46,"value":213},"The column order doesn't matter — the skill matches by header name. The file may contain additional columns (like the \"Address details\" enrichment columns from the export skill) — those are ignored.",{"type":40,"tag":49,"props":215,"children":217},{"id":216},"workflow",[218],{"type":46,"value":219},"Workflow",{"type":40,"tag":221,"props":222,"children":224},"h3",{"id":223},"step-1-authenticate",[225],{"type":46,"value":226},"Step 1 — Authenticate",{"type":40,"tag":56,"props":228,"children":229},{},[230,232,238],{"type":46,"value":231},"Call ",{"type":40,"tag":81,"props":233,"children":235},{"className":234},[],[236],{"type":46,"value":237},"get_viewer",{"type":46,"value":239}," (no arguments) to confirm the session is active and note the organization name.",{"type":40,"tag":221,"props":241,"children":243},{"id":242},"step-2-read-and-parse-the-file",[244],{"type":46,"value":245},"Step 2 — Read and parse the file",{"type":40,"tag":56,"props":247,"children":248},{},[249],{"type":46,"value":250},"The user will provide a CSV or XLSX file. Detect the format from the file extension.",{"type":40,"tag":56,"props":252,"children":253},{},[254,259,261,267],{"type":40,"tag":73,"props":255,"children":256},{},[257],{"type":46,"value":258},"For XLSX files:",{"type":46,"value":260},"\nUse Python with ",{"type":40,"tag":81,"props":262,"children":264},{"className":263},[],[265],{"type":46,"value":266},"openpyxl==3.1.5",{"type":46,"value":268},". If not installed, stop and display:",{"type":40,"tag":270,"props":271,"children":272},"blockquote",{},[273],{"type":40,"tag":56,"props":274,"children":275},{},[276,278,284],{"type":46,"value":277},"\"openpyxl is not installed. Please run: ",{"type":40,"tag":81,"props":279,"children":281},{"className":280},[],[282],{"type":46,"value":283},"python3 -m venv .venv && .venv\u002Fbin\u002Fpip install openpyxl==3.1.5",{"type":46,"value":285},"\"",{"type":40,"tag":170,"props":287,"children":288},{},[289,294,299,304],{"type":40,"tag":174,"props":290,"children":291},{},[292],{"type":46,"value":293},"If the workbook has multiple sheets, look for one named \"Contacts\" (case-insensitive). If not found, use the first sheet.",{"type":40,"tag":174,"props":295,"children":296},{},[297],{"type":46,"value":298},"Read the header row to find the column indices for \"Contact Name\", \"Contact Address\", and \"Contact Tag\" (match case-insensitively, trim whitespace).",{"type":40,"tag":174,"props":300,"children":301},{},[302],{"type":46,"value":303},"Read all data rows.",{"type":40,"tag":174,"props":305,"children":306},{},[307,309,315,317,322],{"type":46,"value":308},"Filter out trailing empty rows — openpyxl sometimes reads extra ",{"type":40,"tag":81,"props":310,"children":312},{"className":311},[],[313],{"type":46,"value":314},"None",{"type":46,"value":316}," rows at the bottom of the sheet. Skip any row where all cells are ",{"type":40,"tag":81,"props":318,"children":320},{"className":319},[],[321],{"type":46,"value":314},{"type":46,"value":323}," or empty strings.",{"type":40,"tag":56,"props":325,"children":326},{},[327,332,334,340],{"type":40,"tag":73,"props":328,"children":329},{},[330],{"type":46,"value":331},"For CSV files:",{"type":46,"value":333},"\nUse Python's built-in ",{"type":40,"tag":81,"props":335,"children":337},{"className":336},[],[338],{"type":46,"value":339},"csv",{"type":46,"value":341}," module.",{"type":40,"tag":170,"props":343,"children":344},{},[345,366,371,376],{"type":40,"tag":174,"props":346,"children":347},{},[348,350,356,358,364],{"type":46,"value":349},"Open the file with ",{"type":40,"tag":81,"props":351,"children":353},{"className":352},[],[354],{"type":46,"value":355},"encoding='utf-8-sig'",{"type":46,"value":357}," to handle the BOM (byte-order mark) that Excel adds when saving CSVs. Without this, the first header may appear as ",{"type":40,"tag":81,"props":359,"children":361},{"className":360},[],[362],{"type":46,"value":363},"\\ufeffContact Name",{"type":46,"value":365}," and won't match.",{"type":40,"tag":174,"props":367,"children":368},{},[369],{"type":46,"value":370},"Try comma delimiter first. If the header row doesn't contain the expected columns, retry with semicolon delimiter (common in European-locale Excel exports).",{"type":40,"tag":174,"props":372,"children":373},{},[374],{"type":46,"value":375},"Read the header row to find column indices, same matching logic as above.",{"type":40,"tag":174,"props":377,"children":378},{},[379],{"type":46,"value":303},{"type":40,"tag":221,"props":381,"children":383},{"id":382},"step-3-validate-and-filter",[384],{"type":46,"value":385},"Step 3 — Validate and filter",{"type":40,"tag":56,"props":387,"children":388},{},[389],{"type":46,"value":390},"For each row:",{"type":40,"tag":392,"props":393,"children":394},"ol",{},[395,405,415,425,435],{"type":40,"tag":174,"props":396,"children":397},{},[398,403],{"type":40,"tag":73,"props":399,"children":400},{},[401],{"type":46,"value":402},"Skip if Contact Name is blank or missing",{"type":46,"value":404}," — the user hasn't identified this address yet, so there's nothing to import.",{"type":40,"tag":174,"props":406,"children":407},{},[408,413],{"type":40,"tag":73,"props":409,"children":410},{},[411],{"type":46,"value":412},"Skip if Contact Address is blank or missing",{"type":46,"value":414}," — can't label an address that doesn't exist.",{"type":40,"tag":174,"props":416,"children":417},{},[418,423],{"type":40,"tag":73,"props":419,"children":420},{},[421],{"type":46,"value":422},"Trim whitespace",{"type":46,"value":424}," from name, address, and tag values.",{"type":40,"tag":174,"props":426,"children":427},{},[428,433],{"type":40,"tag":73,"props":429,"children":430},{},[431],{"type":46,"value":432},"Parse tags",{"type":46,"value":434},": if Contact Tag contains commas, split into multiple tags. Trim each tag. Remove empty strings.",{"type":40,"tag":174,"props":436,"children":437},{},[438,443],{"type":40,"tag":73,"props":439,"children":440},{},[441],{"type":46,"value":442},"Deduplicate by address",{"type":46,"value":444}," (case-insensitive): if the same address appears multiple times, keep the last occurrence (the user likely corrected it). Warn about duplicates.",{"type":40,"tag":56,"props":446,"children":447},{},[448],{"type":46,"value":449},"After filtering, report to the user:",{"type":40,"tag":170,"props":451,"children":452},{},[453,458,463,468],{"type":40,"tag":174,"props":454,"children":455},{},[456],{"type":46,"value":457},"Total rows in the file",{"type":40,"tag":174,"props":459,"children":460},{},[461],{"type":46,"value":462},"Rows skipped (blank name)",{"type":40,"tag":174,"props":464,"children":465},{},[466],{"type":46,"value":467},"Rows to import",{"type":40,"tag":174,"props":469,"children":470},{},[471],{"type":46,"value":472},"Any duplicates found",{"type":40,"tag":56,"props":474,"children":475},{},[476],{"type":46,"value":477},"Then show a preview table of the first 10 rows that will be imported (address, name, tags) and ask the user to confirm before proceeding.",{"type":40,"tag":221,"props":479,"children":481},{"id":480},"step-4-check-existing-contacts-only-for-imports-with-50-rows",[482],{"type":46,"value":483},"Step 4 — Check existing contacts (only for imports with 50+ rows)",{"type":40,"tag":56,"props":485,"children":486},{},[487],{"type":46,"value":488},"For small imports (\u003C50 rows), skip this step and go straight to importing — fetching the entire address book for a handful of contacts adds unnecessary delay.",{"type":40,"tag":56,"props":490,"children":491},{},[492],{"type":46,"value":493},"For larger imports, fetch the current address book to identify which addresses already have labels. This helps the user understand what will be created vs. updated.",{"type":40,"tag":495,"props":496,"children":501},"pre",{"className":497,"code":498,"language":499,"meta":500,"style":500},"language-graphql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","query AddressBook($limit: Int, $offset: Int) {\n  customAccountNameLabel(limit: $limit, offset: $offset) {\n    results {\n      originalIdentifier\n      labelValue\n      tags\n    }\n    totalCount\n  }\n}\n","graphql","",[502],{"type":40,"tag":81,"props":503,"children":504},{"__ignoreMap":500},[505,516,525,534,543,552,561,570,579,588],{"type":40,"tag":506,"props":507,"children":510},"span",{"class":508,"line":509},"line",1,[511],{"type":40,"tag":506,"props":512,"children":513},{},[514],{"type":46,"value":515},"query AddressBook($limit: Int, $offset: Int) {\n",{"type":40,"tag":506,"props":517,"children":519},{"class":508,"line":518},2,[520],{"type":40,"tag":506,"props":521,"children":522},{},[523],{"type":46,"value":524},"  customAccountNameLabel(limit: $limit, offset: $offset) {\n",{"type":40,"tag":506,"props":526,"children":528},{"class":508,"line":527},3,[529],{"type":40,"tag":506,"props":530,"children":531},{},[532],{"type":46,"value":533},"    results {\n",{"type":40,"tag":506,"props":535,"children":537},{"class":508,"line":536},4,[538],{"type":40,"tag":506,"props":539,"children":540},{},[541],{"type":46,"value":542},"      originalIdentifier\n",{"type":40,"tag":506,"props":544,"children":546},{"class":508,"line":545},5,[547],{"type":40,"tag":506,"props":548,"children":549},{},[550],{"type":46,"value":551},"      labelValue\n",{"type":40,"tag":506,"props":553,"children":555},{"class":508,"line":554},6,[556],{"type":40,"tag":506,"props":557,"children":558},{},[559],{"type":46,"value":560},"      tags\n",{"type":40,"tag":506,"props":562,"children":564},{"class":508,"line":563},7,[565],{"type":40,"tag":506,"props":566,"children":567},{},[568],{"type":46,"value":569},"    }\n",{"type":40,"tag":506,"props":571,"children":573},{"class":508,"line":572},8,[574],{"type":40,"tag":506,"props":575,"children":576},{},[577],{"type":46,"value":578},"    totalCount\n",{"type":40,"tag":506,"props":580,"children":582},{"class":508,"line":581},9,[583],{"type":40,"tag":506,"props":584,"children":585},{},[586],{"type":46,"value":587},"  }\n",{"type":40,"tag":506,"props":589,"children":591},{"class":508,"line":590},10,[592],{"type":40,"tag":506,"props":593,"children":594},{},[595],{"type":46,"value":596},"}\n",{"type":40,"tag":56,"props":598,"children":599},{},[600,602,608],{"type":46,"value":601},"Paginate through all results (500 at a time). Build a lookup dictionary keyed by ",{"type":40,"tag":81,"props":603,"children":605},{"className":604},[],[606],{"type":46,"value":607},"originalIdentifier.lower()",{"type":46,"value":609},".",{"type":40,"tag":56,"props":611,"children":612},{},[613],{"type":46,"value":614},"Compare against the import list and report:",{"type":40,"tag":170,"props":616,"children":617},{},[618,628],{"type":40,"tag":174,"props":619,"children":620},{},[621,626],{"type":40,"tag":73,"props":622,"children":623},{},[624],{"type":46,"value":625},"New contacts",{"type":46,"value":627}," — addresses not in the current address book",{"type":40,"tag":174,"props":629,"children":630},{},[631,636],{"type":40,"tag":73,"props":632,"children":633},{},[634],{"type":46,"value":635},"Updates",{"type":46,"value":637}," — addresses that already exist but will get a new name or tags",{"type":40,"tag":56,"props":639,"children":640},{},[641,646,648,654,656,661,663,669,671,677],{"type":40,"tag":73,"props":642,"children":643},{},[644],{"type":46,"value":645},"Important",{"type":46,"value":647},": ",{"type":40,"tag":81,"props":649,"children":651},{"className":650},[],[652],{"type":46,"value":653},"setCustomAccountNameLabelTags",{"type":46,"value":655}," is a ",{"type":40,"tag":73,"props":657,"children":658},{},[659],{"type":46,"value":660},"replace",{"type":46,"value":662}," operation, not append. If a contact currently has tags ",{"type":40,"tag":81,"props":664,"children":666},{"className":665},[],[667],{"type":46,"value":668},"[\"Exchange\", \"Custodian\"]",{"type":46,"value":670}," and the import file has ",{"type":40,"tag":81,"props":672,"children":674},{"className":673},[],[675],{"type":46,"value":676},"[\"Vendor\"]",{"type":46,"value":678},", the old tags will be overwritten. Warn the user about this for any contacts being updated that already have tags.",{"type":40,"tag":56,"props":680,"children":681},{},[682],{"type":46,"value":683},"Present this breakdown to the user before proceeding. If there are updates, list a few examples showing old name → new name so the user can sanity-check.",{"type":40,"tag":221,"props":685,"children":687},{"id":686},"step-5-import-contacts",[688],{"type":46,"value":689},"Step 5 — Import contacts",{"type":40,"tag":56,"props":691,"children":692},{},[693],{"type":46,"value":694},"For each validated row, make two API calls:",{"type":40,"tag":56,"props":696,"children":697},{},[698],{"type":40,"tag":73,"props":699,"children":700},{},[701],{"type":46,"value":702},"5a. Set the contact name:",{"type":40,"tag":495,"props":704,"children":706},{"className":497,"code":705,"language":499,"meta":500,"style":500},"mutation SetContactName($identifier: String, $labelValue: String) {\n  setCustomAccountName(identifier: $identifier, labelValue: $labelValue) {\n    accountTxsSummary {\n      accountIdentifier\n      displayName\n    }\n  }\n}\n",[707],{"type":40,"tag":81,"props":708,"children":709},{"__ignoreMap":500},[710,718,726,734,742,750,757,764],{"type":40,"tag":506,"props":711,"children":712},{"class":508,"line":509},[713],{"type":40,"tag":506,"props":714,"children":715},{},[716],{"type":46,"value":717},"mutation SetContactName($identifier: String, $labelValue: String) {\n",{"type":40,"tag":506,"props":719,"children":720},{"class":508,"line":518},[721],{"type":40,"tag":506,"props":722,"children":723},{},[724],{"type":46,"value":725},"  setCustomAccountName(identifier: $identifier, labelValue: $labelValue) {\n",{"type":40,"tag":506,"props":727,"children":728},{"class":508,"line":527},[729],{"type":40,"tag":506,"props":730,"children":731},{},[732],{"type":46,"value":733},"    accountTxsSummary {\n",{"type":40,"tag":506,"props":735,"children":736},{"class":508,"line":536},[737],{"type":40,"tag":506,"props":738,"children":739},{},[740],{"type":46,"value":741},"      accountIdentifier\n",{"type":40,"tag":506,"props":743,"children":744},{"class":508,"line":545},[745],{"type":40,"tag":506,"props":746,"children":747},{},[748],{"type":46,"value":749},"      displayName\n",{"type":40,"tag":506,"props":751,"children":752},{"class":508,"line":554},[753],{"type":40,"tag":506,"props":754,"children":755},{},[756],{"type":46,"value":569},{"type":40,"tag":506,"props":758,"children":759},{"class":508,"line":563},[760],{"type":40,"tag":506,"props":761,"children":762},{},[763],{"type":46,"value":587},{"type":40,"tag":506,"props":765,"children":766},{"class":508,"line":572},[767],{"type":40,"tag":506,"props":768,"children":769},{},[770],{"type":46,"value":596},{"type":40,"tag":56,"props":772,"children":773},{},[774],{"type":46,"value":775},"Variables:",{"type":40,"tag":495,"props":777,"children":781},{"className":778,"code":779,"language":780,"meta":500,"style":500},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"identifier\": \"\u003Cthe address>\",\n  \"labelValue\": \"\u003Cthe contact name>\"\n}\n","json",[782],{"type":40,"tag":81,"props":783,"children":784},{"__ignoreMap":500},[785,794,837,871],{"type":40,"tag":506,"props":786,"children":787},{"class":508,"line":509},[788],{"type":40,"tag":506,"props":789,"children":791},{"style":790},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[792],{"type":46,"value":793},"{\n",{"type":40,"tag":506,"props":795,"children":796},{"class":508,"line":518},[797,802,808,812,817,822,828,832],{"type":40,"tag":506,"props":798,"children":799},{"style":790},[800],{"type":46,"value":801},"  \"",{"type":40,"tag":506,"props":803,"children":805},{"style":804},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[806],{"type":46,"value":807},"identifier",{"type":40,"tag":506,"props":809,"children":810},{"style":790},[811],{"type":46,"value":285},{"type":40,"tag":506,"props":813,"children":814},{"style":790},[815],{"type":46,"value":816},":",{"type":40,"tag":506,"props":818,"children":819},{"style":790},[820],{"type":46,"value":821}," \"",{"type":40,"tag":506,"props":823,"children":825},{"style":824},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[826],{"type":46,"value":827},"\u003Cthe address>",{"type":40,"tag":506,"props":829,"children":830},{"style":790},[831],{"type":46,"value":285},{"type":40,"tag":506,"props":833,"children":834},{"style":790},[835],{"type":46,"value":836},",\n",{"type":40,"tag":506,"props":838,"children":839},{"class":508,"line":527},[840,844,849,853,857,861,866],{"type":40,"tag":506,"props":841,"children":842},{"style":790},[843],{"type":46,"value":801},{"type":40,"tag":506,"props":845,"children":846},{"style":804},[847],{"type":46,"value":848},"labelValue",{"type":40,"tag":506,"props":850,"children":851},{"style":790},[852],{"type":46,"value":285},{"type":40,"tag":506,"props":854,"children":855},{"style":790},[856],{"type":46,"value":816},{"type":40,"tag":506,"props":858,"children":859},{"style":790},[860],{"type":46,"value":821},{"type":40,"tag":506,"props":862,"children":863},{"style":824},[864],{"type":46,"value":865},"\u003Cthe contact name>",{"type":40,"tag":506,"props":867,"children":868},{"style":790},[869],{"type":46,"value":870},"\"\n",{"type":40,"tag":506,"props":872,"children":873},{"class":508,"line":536},[874],{"type":40,"tag":506,"props":875,"children":876},{"style":790},[877],{"type":46,"value":596},{"type":40,"tag":56,"props":879,"children":880},{},[881],{"type":40,"tag":73,"props":882,"children":883},{},[884],{"type":46,"value":885},"5b. Set tags (only if the row has tags):",{"type":40,"tag":495,"props":887,"children":889},{"className":497,"code":888,"language":499,"meta":500,"style":500},"mutation SetContactTags($identifier: String!, $tags: [String]!) {\n  setCustomAccountNameLabelTags(identifier: $identifier, tags: $tags) {\n    accountTxsSummary {\n      accountIdentifier\n      displayName\n    }\n  }\n}\n",[890],{"type":40,"tag":81,"props":891,"children":892},{"__ignoreMap":500},[893,901,909,916,923,930,937,944],{"type":40,"tag":506,"props":894,"children":895},{"class":508,"line":509},[896],{"type":40,"tag":506,"props":897,"children":898},{},[899],{"type":46,"value":900},"mutation SetContactTags($identifier: String!, $tags: [String]!) {\n",{"type":40,"tag":506,"props":902,"children":903},{"class":508,"line":518},[904],{"type":40,"tag":506,"props":905,"children":906},{},[907],{"type":46,"value":908},"  setCustomAccountNameLabelTags(identifier: $identifier, tags: $tags) {\n",{"type":40,"tag":506,"props":910,"children":911},{"class":508,"line":527},[912],{"type":40,"tag":506,"props":913,"children":914},{},[915],{"type":46,"value":733},{"type":40,"tag":506,"props":917,"children":918},{"class":508,"line":536},[919],{"type":40,"tag":506,"props":920,"children":921},{},[922],{"type":46,"value":741},{"type":40,"tag":506,"props":924,"children":925},{"class":508,"line":545},[926],{"type":40,"tag":506,"props":927,"children":928},{},[929],{"type":46,"value":749},{"type":40,"tag":506,"props":931,"children":932},{"class":508,"line":554},[933],{"type":40,"tag":506,"props":934,"children":935},{},[936],{"type":46,"value":569},{"type":40,"tag":506,"props":938,"children":939},{"class":508,"line":563},[940],{"type":40,"tag":506,"props":941,"children":942},{},[943],{"type":46,"value":587},{"type":40,"tag":506,"props":945,"children":946},{"class":508,"line":572},[947],{"type":40,"tag":506,"props":948,"children":949},{},[950],{"type":46,"value":596},{"type":40,"tag":56,"props":952,"children":953},{},[954],{"type":46,"value":775},{"type":40,"tag":495,"props":956,"children":958},{"className":778,"code":957,"language":780,"meta":500,"style":500},"{\n  \"identifier\": \"\u003Cthe address>\",\n  \"tags\": [\"Exchange\", \"Custodian\"]\n}\n",[959],{"type":40,"tag":81,"props":960,"children":961},{"__ignoreMap":500},[962,969,1004,1064],{"type":40,"tag":506,"props":963,"children":964},{"class":508,"line":509},[965],{"type":40,"tag":506,"props":966,"children":967},{"style":790},[968],{"type":46,"value":793},{"type":40,"tag":506,"props":970,"children":971},{"class":508,"line":518},[972,976,980,984,988,992,996,1000],{"type":40,"tag":506,"props":973,"children":974},{"style":790},[975],{"type":46,"value":801},{"type":40,"tag":506,"props":977,"children":978},{"style":804},[979],{"type":46,"value":807},{"type":40,"tag":506,"props":981,"children":982},{"style":790},[983],{"type":46,"value":285},{"type":40,"tag":506,"props":985,"children":986},{"style":790},[987],{"type":46,"value":816},{"type":40,"tag":506,"props":989,"children":990},{"style":790},[991],{"type":46,"value":821},{"type":40,"tag":506,"props":993,"children":994},{"style":824},[995],{"type":46,"value":827},{"type":40,"tag":506,"props":997,"children":998},{"style":790},[999],{"type":46,"value":285},{"type":40,"tag":506,"props":1001,"children":1002},{"style":790},[1003],{"type":46,"value":836},{"type":40,"tag":506,"props":1005,"children":1006},{"class":508,"line":527},[1007,1011,1016,1020,1024,1029,1033,1037,1041,1046,1050,1055,1059],{"type":40,"tag":506,"props":1008,"children":1009},{"style":790},[1010],{"type":46,"value":801},{"type":40,"tag":506,"props":1012,"children":1013},{"style":804},[1014],{"type":46,"value":1015},"tags",{"type":40,"tag":506,"props":1017,"children":1018},{"style":790},[1019],{"type":46,"value":285},{"type":40,"tag":506,"props":1021,"children":1022},{"style":790},[1023],{"type":46,"value":816},{"type":40,"tag":506,"props":1025,"children":1026},{"style":790},[1027],{"type":46,"value":1028}," [",{"type":40,"tag":506,"props":1030,"children":1031},{"style":790},[1032],{"type":46,"value":285},{"type":40,"tag":506,"props":1034,"children":1035},{"style":824},[1036],{"type":46,"value":150},{"type":40,"tag":506,"props":1038,"children":1039},{"style":790},[1040],{"type":46,"value":285},{"type":40,"tag":506,"props":1042,"children":1043},{"style":790},[1044],{"type":46,"value":1045},",",{"type":40,"tag":506,"props":1047,"children":1048},{"style":790},[1049],{"type":46,"value":821},{"type":40,"tag":506,"props":1051,"children":1052},{"style":824},[1053],{"type":46,"value":1054},"Custodian",{"type":40,"tag":506,"props":1056,"children":1057},{"style":790},[1058],{"type":46,"value":285},{"type":40,"tag":506,"props":1060,"children":1061},{"style":790},[1062],{"type":46,"value":1063},"]\n",{"type":40,"tag":506,"props":1065,"children":1066},{"class":508,"line":536},[1067],{"type":40,"tag":506,"props":1068,"children":1069},{"style":790},[1070],{"type":46,"value":596},{"type":40,"tag":56,"props":1072,"children":1073},{},[1074,1079],{"type":40,"tag":73,"props":1075,"children":1076},{},[1077],{"type":46,"value":1078},"Batching strategy:",{"type":46,"value":1080}," The API doesn't have a bulk endpoint. For small imports (\u003C20 contacts), fire all name mutations in parallel, then all tag mutations in parallel — this is fast and safe. For larger imports, process in batches of 10 parallel calls to avoid overwhelming the API, and show progress to the user every 25 contacts (e.g. \"Imported 25\u002F142...\").",{"type":40,"tag":56,"props":1082,"children":1083},{},[1084,1089],{"type":40,"tag":73,"props":1085,"children":1086},{},[1087],{"type":46,"value":1088},"Error handling:",{"type":46,"value":1090}," If a mutation fails for a specific address, log the error and continue with the next row. Don't stop the entire import. Collect all failures for the summary.",{"type":40,"tag":221,"props":1092,"children":1094},{"id":1093},"step-6-summary",[1095],{"type":46,"value":1096},"Step 6 — Summary",{"type":40,"tag":56,"props":1098,"children":1099},{},[1100],{"type":46,"value":1101},"After all mutations complete, present a summary:",{"type":40,"tag":170,"props":1103,"children":1104},{},[1105,1110,1115],{"type":40,"tag":174,"props":1106,"children":1107},{},[1108],{"type":46,"value":1109},"Total contacts imported successfully (name set)",{"type":40,"tag":174,"props":1111,"children":1112},{},[1113],{"type":46,"value":1114},"Total contacts with tags set",{"type":40,"tag":174,"props":1116,"children":1117},{},[1118],{"type":46,"value":1119},"Any failures (list the address and error message)",{"type":40,"tag":56,"props":1121,"children":1122},{},[1123],{"type":46,"value":1124},"If there were failures, suggest the user can retry by running the skill again with the same file — already-imported contacts will just be updated (the mutation is idempotent for names).",{"type":40,"tag":49,"props":1126,"children":1128},{"id":1127},"edge-cases",[1129],{"type":46,"value":1130},"Edge Cases",{"type":40,"tag":170,"props":1132,"children":1133},{},[1134,1144,1154,1164,1174,1184,1194,1212,1235,1245,1261,1285,1310],{"type":40,"tag":174,"props":1135,"children":1136},{},[1137,1142],{"type":40,"tag":73,"props":1138,"children":1139},{},[1140],{"type":46,"value":1141},"File has no header row",{"type":46,"value":1143},": If the first row doesn't contain recognizable column names, tell the user the expected format and ask them to fix the file.",{"type":40,"tag":174,"props":1145,"children":1146},{},[1147,1152],{"type":40,"tag":73,"props":1148,"children":1149},{},[1150],{"type":46,"value":1151},"File has wrong columns",{"type":46,"value":1153},": If \"Contact Address\" column can't be found, the file isn't usable. Report which columns were found and what's expected.",{"type":40,"tag":174,"props":1155,"children":1156},{},[1157,1162],{"type":40,"tag":73,"props":1158,"children":1159},{},[1160],{"type":46,"value":1161},"Empty file",{"type":46,"value":1163},": If the file has headers but no data rows, tell the user.",{"type":40,"tag":174,"props":1165,"children":1166},{},[1167,1172],{"type":40,"tag":73,"props":1168,"children":1169},{},[1170],{"type":46,"value":1171},"All names blank",{"type":46,"value":1173},": If every row has a blank Contact Name, tell the user — they probably uploaded the unfilled template by mistake.",{"type":40,"tag":174,"props":1175,"children":1176},{},[1177,1182],{"type":40,"tag":73,"props":1178,"children":1179},{},[1180],{"type":46,"value":1181},"Very large files (>500 rows)",{"type":46,"value":1183},": Warn the user this will take a while and ask for confirmation.",{"type":40,"tag":174,"props":1185,"children":1186},{},[1187,1192],{"type":40,"tag":73,"props":1188,"children":1189},{},[1190],{"type":46,"value":1191},"Special characters in names",{"type":46,"value":1193},": Pass through as-is — TRES handles unicode in labels.",{"type":40,"tag":174,"props":1195,"children":1196},{},[1197,1202,1204,1210],{"type":40,"tag":73,"props":1198,"children":1199},{},[1200],{"type":46,"value":1201},"Mixed case addresses",{"type":46,"value":1203},": The ",{"type":40,"tag":81,"props":1205,"children":1207},{"className":1206},[],[1208],{"type":46,"value":1209},"setCustomAccountName",{"type":46,"value":1211}," mutation accepts addresses as-is. Don't normalize case — pass the original value from the file.",{"type":40,"tag":174,"props":1213,"children":1214},{},[1215,1220,1221,1227,1229,1234],{"type":40,"tag":73,"props":1216,"children":1217},{},[1218],{"type":46,"value":1219},"Tag with extra spaces",{"type":46,"value":647},{"type":40,"tag":81,"props":1222,"children":1224},{"className":1223},[],[1225],{"type":46,"value":1226},"\" Exchange , Custodian \"",{"type":46,"value":1228}," → trim to ",{"type":40,"tag":81,"props":1230,"children":1232},{"className":1231},[],[1233],{"type":46,"value":668},{"type":46,"value":609},{"type":40,"tag":174,"props":1236,"children":1237},{},[1238,1243],{"type":40,"tag":73,"props":1239,"children":1240},{},[1241],{"type":46,"value":1242},"Address not in any transaction",{"type":46,"value":1244},": The mutations work even for addresses that TRES has never seen in a transaction — they create the address book entry regardless.",{"type":40,"tag":174,"props":1246,"children":1247},{},[1248,1253,1254,1259],{"type":40,"tag":73,"props":1249,"children":1250},{},[1251],{"type":46,"value":1252},"Tags overwrite",{"type":46,"value":647},{"type":40,"tag":81,"props":1255,"children":1257},{"className":1256},[],[1258],{"type":46,"value":653},{"type":46,"value":1260}," replaces existing tags entirely. If the user only wants to add tags, they would need to merge with existing tags first (the skill does not do this automatically — it's a replace operation). Flag this to the user when updating existing contacts that already have tags.",{"type":40,"tag":174,"props":1262,"children":1263},{},[1264,1269,1271,1277,1279,1284],{"type":40,"tag":73,"props":1265,"children":1266},{},[1267],{"type":46,"value":1268},"CSV BOM prefix",{"type":46,"value":1270},": Excel-saved CSVs include a UTF-8 BOM (",{"type":40,"tag":81,"props":1272,"children":1274},{"className":1273},[],[1275],{"type":46,"value":1276},"\\ufeff",{"type":46,"value":1278},") that corrupts the first column header. Always open CSVs with ",{"type":40,"tag":81,"props":1280,"children":1282},{"className":1281},[],[1283],{"type":46,"value":355},{"type":46,"value":609},{"type":40,"tag":174,"props":1286,"children":1287},{},[1288,1293,1295,1301,1303,1308],{"type":40,"tag":73,"props":1289,"children":1290},{},[1291],{"type":46,"value":1292},"Semicolon-delimited CSVs",{"type":46,"value":1294},": European Excel exports use ",{"type":40,"tag":81,"props":1296,"children":1298},{"className":1297},[],[1299],{"type":46,"value":1300},";",{"type":46,"value":1302}," instead of ",{"type":40,"tag":81,"props":1304,"children":1306},{"className":1305},[],[1307],{"type":46,"value":1045},{"type":46,"value":1309},". If comma parsing doesn't find the expected headers, retry with semicolon.",{"type":40,"tag":174,"props":1311,"children":1312},{},[1313,1318,1320,1325],{"type":40,"tag":73,"props":1314,"children":1315},{},[1316],{"type":46,"value":1317},"Trailing empty rows in XLSX",{"type":46,"value":1319},": openpyxl reads empty rows at the end of the sheet. Skip rows where all values are ",{"type":40,"tag":81,"props":1321,"children":1323},{"className":1322},[],[1324],{"type":46,"value":314},{"type":46,"value":1326}," or empty.",{"type":40,"tag":1328,"props":1329,"children":1330},"style",{},[1331],{"type":46,"value":1332},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1334,"total":1434},[1335,1354,1373,1387,1397,1409,1421],{"slug":1336,"name":1336,"fn":1337,"description":1338,"org":1339,"tags":1340,"stars":23,"repoUrl":24,"updatedAt":1353},"quickdesign","generate AI media assets","Use the `quickdesign` CLI to generate AI media — UGC promo videos, image edits, product creatives, video upscales — through Seedance, Kling, Sora2, Nano Banana, and GPT Image. Invoke this skill whenever the user asks for a talking-avatar video, multi-segment ad \u002F promo \u002F explainer, image edit (object swap, angle change, state change), product photoshoot, or video upscale via QuickDesign.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1341,1344,1347,1350],{"name":1342,"slug":1343,"type":16},"Creative","creative",{"name":1345,"slug":1346,"type":16},"Image Generation","image-generation",{"name":1348,"slug":1349,"type":16},"Marketing","marketing",{"name":1351,"slug":1352,"type":16},"Video","video","2026-07-01T08:09:32.316182",{"slug":1355,"name":1355,"fn":1356,"description":1357,"org":1358,"tags":1359,"stars":23,"repoUrl":24,"updatedAt":1372},"testdino-audit","audit Playwright test code","Use only when the user explicitly asks for a TestDino audit of Playwright automated test code. Routes through the audit tools the TestDino MCP server exposes (get_audit_report + submit_audit_report, or the legacy test_audit). For generic code review or non-Playwright targets, do a normal review instead.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1360,1363,1366,1369],{"name":1361,"slug":1362,"type":16},"Audit","audit",{"name":1364,"slug":1365,"type":16},"Code Analysis","code-analysis",{"name":1367,"slug":1368,"type":16},"Playwright","playwright",{"name":1370,"slug":1371,"type":16},"Testing","testing","2026-07-02T07:37:17.341081",{"slug":1374,"name":1374,"fn":1375,"description":1376,"org":1377,"tags":1378,"stars":23,"repoUrl":24,"updatedAt":1386},"testdino-health","manage TestDino connection status","Use when the user wants to check TestDino connection status, validate their PAT, discover available organizations and projects, or find the right projectId. Always call this first when the project context is ambiguous before any other TestDino tool.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1379,1382,1385],{"name":1380,"slug":1381,"type":16},"Monitoring","monitoring",{"name":1383,"slug":1384,"type":16},"QA","qa",{"name":1370,"slug":1371,"type":16},"2026-07-02T07:37:18.566504",{"slug":1388,"name":1388,"fn":1389,"description":1390,"org":1391,"tags":1392,"stars":23,"repoUrl":24,"updatedAt":1396},"testdino-manual-runs","manage manual QA execution runs in TestDino","Use when the user wants to manage a manual execution run or update case-level results inside a run — listing runs, creating runs for a release, inspecting a run, assigning cases, or marking case results (passed\u002Ffailed\u002Fblocked\u002Fskipped\u002Fretest\u002Funtested). Accepts counter-style IDs like RUN-12 and TC-156.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1393,1394,1395],{"name":14,"slug":15,"type":16},{"name":1383,"slug":1384,"type":16},{"name":1370,"slug":1371,"type":16},"2026-07-02T07:37:23.446065",{"slug":1398,"name":1398,"fn":1399,"description":1400,"org":1401,"tags":1402,"stars":23,"repoUrl":24,"updatedAt":1408},"testdino-manual-tests","manage manual QA test cases in TestDino","Use when the user wants to create, update, or browse manual QA test cases and suites in TestDino — not execution runs. Covers list_manual_test_suites, list_manual_test_cases, get_manual_test_case, create_manual_test_case, update_manual_test_case, create_manual_test_suite.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1403,1406,1407],{"name":1404,"slug":1405,"type":16},"Documentation","documentation",{"name":1383,"slug":1384,"type":16},{"name":1370,"slug":1371,"type":16},"2026-07-02T07:37:22.247052",{"slug":1410,"name":1410,"fn":1411,"description":1412,"org":1413,"tags":1414,"stars":23,"repoUrl":24,"updatedAt":1420},"testdino-releases","manage TestDino releases and milestones","Use when the user wants to browse, inspect, create, or update releases\u002Fmilestones in a TestDino project. Covers list_releases, get_release, create_release, and update_release. Accepts counter-style IDs like MS-12.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1415,1418,1419],{"name":1416,"slug":1417,"type":16},"Project Management","project-management",{"name":1383,"slug":1384,"type":16},{"name":1370,"slug":1371,"type":16},"2026-07-02T07:37:19.793846",{"slug":1422,"name":1422,"fn":1423,"description":1424,"org":1425,"tags":1426,"stars":23,"repoUrl":24,"updatedAt":1433},"testdino-runs","inspect automated test runs","Use when the user wants to inspect automated test runs, list failed or flaky tests, debug a failing testcase with historical context, or filter runs by branch, commit, author, environment, browser, status, or tags. Includes list_testruns, get_run_details, list_testcase, get_testcase_details, and debug_testcase.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1427,1430,1431,1432],{"name":1428,"slug":1429,"type":16},"Debugging","debugging",{"name":1367,"slug":1368,"type":16},{"name":1383,"slug":1384,"type":16},{"name":1370,"slug":1371,"type":16},"2026-07-02T07:37:16.07175",30,{"items":1436,"total":1619},[1437,1456,1470,1482,1501,1512,1533,1553,1567,1582,1590,1603],{"slug":1438,"name":1438,"fn":1439,"description":1440,"org":1441,"tags":1442,"stars":1453,"repoUrl":1454,"updatedAt":1455},"algorithmic-art","create algorithmic art with p5.js","Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1443,1444,1447,1450],{"name":1342,"slug":1343,"type":16},{"name":1445,"slug":1446,"type":16},"Design","design",{"name":1448,"slug":1449,"type":16},"Generative Art","generative-art",{"name":1451,"slug":1452,"type":16},"JavaScript","javascript",161831,"https:\u002F\u002Fgithub.com\u002Fanthropics\u002Fskills","2026-04-06T17:56:15.455818",{"slug":1457,"name":1457,"fn":1458,"description":1459,"org":1460,"tags":1461,"stars":1453,"repoUrl":1454,"updatedAt":1469},"brand-guidelines","apply Anthropic brand colors and typography","Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1462,1465,1466],{"name":1463,"slug":1464,"type":16},"Branding","branding",{"name":1445,"slug":1446,"type":16},{"name":1467,"slug":1468,"type":16},"Typography","typography","2026-04-06T17:56:05.042852",{"slug":1471,"name":1471,"fn":1472,"description":1473,"org":1474,"tags":1475,"stars":1453,"repoUrl":1454,"updatedAt":1481},"canvas-design","create posters and visual art as PNG or PDF","Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1476,1477,1478],{"name":1342,"slug":1343,"type":16},{"name":1445,"slug":1446,"type":16},{"name":1479,"slug":1480,"type":16},"PDF","pdf","2026-04-06T17:56:03.794732",{"slug":1483,"name":1483,"fn":1484,"description":1485,"org":1486,"tags":1487,"stars":1453,"repoUrl":1454,"updatedAt":1500},"claude-api","build apps with the Claude API","Reference for the Claude API \u002F Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude\u002FAnthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing\u002Fmodel choice\u002Flimits\u002Fcaching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent\u002FMCP\u002Ftool-definition\u002Fmulti-agent\u002FRAG\u002FLLM-judge\u002Fcomputer-use; generate\u002Fsummarize\u002Fextract\u002Fclassify\u002Frewrite\u002Fconverse over NL; debugging refusals\u002Fcutoffs\u002Fstreaming\u002Ftool-calls\u002Ftokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI\u002FGPT\u002FGemini\u002FLlama\u002FMistral\u002FCohere\u002FOllama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1488,1491,1492,1495,1497],{"name":1489,"slug":1490,"type":16},"Agents","agents",{"name":9,"slug":8,"type":16},{"name":1493,"slug":1494,"type":16},"Anthropic SDK","anthropic-sdk",{"name":1496,"slug":1483,"type":16},"Claude API",{"name":1498,"slug":1499,"type":16},"LLM","llm","2026-07-28T05:36:08.213335",{"slug":1502,"name":1502,"fn":1503,"description":1504,"org":1505,"tags":1506,"stars":1453,"repoUrl":1454,"updatedAt":1511},"doc-coauthoring","co-author documentation and technical specs","Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1507,1508],{"name":1404,"slug":1405,"type":16},{"name":1509,"slug":1510,"type":16},"Technical Writing","technical-writing","2026-04-06T17:56:14.18897",{"slug":1513,"name":1513,"fn":1514,"description":1515,"org":1516,"tags":1517,"stars":1453,"repoUrl":1454,"updatedAt":1532},"docx","create and edit Word documents","Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files) or Word templates (.dotx files). Triggers include: any mention of 'Word doc', 'word document', '.docx', '.dotx', or requests to produce professional documents with formatting like tables of contents, headings, page numbers, or letterheads. Also use when extracting or reorganizing content from .docx or .dotx files, inserting or replacing images in documents, performing find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a 'report', 'memo', 'letter', 'template', or similar deliverable as a Word or .docx file, use this skill. Do NOT use for PDFs, spreadsheets, Google Docs, or general coding tasks unrelated to document generation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1518,1521,1523,1526,1529],{"name":1519,"slug":1520,"type":16},"Documents","documents",{"name":1522,"slug":1513,"type":16},"DOCX",{"name":1524,"slug":1525,"type":16},"Office","office",{"name":1527,"slug":1528,"type":16},"Templates","templates",{"name":1530,"slug":1531,"type":16},"Word","word","2026-07-18T05:16:23.136271",{"slug":1534,"name":1534,"fn":1535,"description":1536,"org":1537,"tags":1538,"stars":1453,"repoUrl":1454,"updatedAt":1552},"frontend-design","design production-grade frontend interfaces","Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1539,1540,1543,1546,1549],{"name":1445,"slug":1446,"type":16},{"name":1541,"slug":1542,"type":16},"Frontend","frontend",{"name":1544,"slug":1545,"type":16},"React","react",{"name":1547,"slug":1548,"type":16},"Tailwind CSS","tailwind-css",{"name":1550,"slug":1551,"type":16},"UI Components","ui-components","2026-04-06T17:56:16.723469",{"slug":1554,"name":1554,"fn":1555,"description":1556,"org":1557,"tags":1558,"stars":1453,"repoUrl":1454,"updatedAt":1566},"internal-comms","write internal company communications","A set of resources to help me write all kinds of internal communications, using the formats that my company likes to use. Claude should use this skill whenever asked to write some sort of internal communications (status reports, leadership updates, 3P updates, company newsletters, FAQs, incident reports, project updates, etc.).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1559,1562,1563],{"name":1560,"slug":1561,"type":16},"Communications","communications",{"name":1527,"slug":1528,"type":16},{"name":1564,"slug":1565,"type":16},"Writing","writing","2026-04-06T17:56:20.695522",{"slug":1568,"name":1568,"fn":1569,"description":1570,"org":1571,"tags":1572,"stars":1453,"repoUrl":1454,"updatedAt":1581},"mcp-builder","build MCP servers","Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node\u002FTypeScript (MCP SDK).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1573,1574,1577,1578],{"name":1489,"slug":1490,"type":16},{"name":1575,"slug":1576,"type":16},"API Development","api-development",{"name":1498,"slug":1499,"type":16},{"name":1579,"slug":1580,"type":16},"MCP","mcp","2026-04-06T17:56:10.357665",{"slug":1480,"name":1480,"fn":1583,"description":1584,"org":1585,"tags":1586,"stars":1453,"repoUrl":1454,"updatedAt":1589},"read edit and manipulate PDF files","Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text\u002Ftables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting\u002Fdecrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1587,1588],{"name":1519,"slug":1520,"type":16},{"name":1479,"slug":1480,"type":16},"2026-04-06T17:56:02.483316",{"slug":1591,"name":1591,"fn":1592,"description":1593,"org":1594,"tags":1595,"stars":1453,"repoUrl":1454,"updatedAt":1602},"pptx","create and edit PowerPoint presentations","Use this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates (.potx), layouts, speaker notes, or comments. Trigger whenever the user mentions \"deck,\" \"slides,\" \"presentation,\" or references a .pptx or .potx filename, regardless of what they plan to do with the content afterward. If a .pptx or .potx file needs to be opened, created, or touched, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1596,1599],{"name":1597,"slug":1598,"type":16},"PowerPoint","powerpoint",{"name":1600,"slug":1601,"type":16},"Presentations","presentations","2026-07-18T05:16:24.1471",{"slug":1604,"name":1604,"fn":1605,"description":1606,"org":1607,"tags":1608,"stars":1453,"repoUrl":1454,"updatedAt":1618},"skill-creator","create and optimize agent skills","Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1609,1610,1611,1614,1617],{"name":1489,"slug":1490,"type":16},{"name":1404,"slug":1405,"type":16},{"name":1612,"slug":1613,"type":16},"Evals","evals",{"name":1615,"slug":1616,"type":16},"Performance","performance",{"name":1509,"slug":1510,"type":16},"2026-04-19T06:45:40.804",490]