create-gtm-skill · git:20261002.59c6320 · 2026-10-02 · sha256 4f090003c434cb4b
create-gtm-skill git:20261002.59c6320A
Immutable. This exact content never changes and is served at /api/v1/blob/4f090003c434cb4b.
--- name: create-gtm-skill description: >- Author a GTM skill in getcargohq/gtm-skills (one-off CLI job or deployed pipeline). Use when creating, adding, scaffolding, or reviewing a new skill, SKILL.md, cookbook, or pipeline folder in gtm-skills. --- # Create a GTM skill This is an authoring skill for people writing this repo. It is not a customer skill and does not install with `npx skills add`. The canonical copy lives in `.agents/skills` so Codex, Claude Code, and Cursor all load the same file (the last two via symlink). Read `CONTRIBUTING.md` first. That file is the contract the validators enforce; this file is the procedure. One folder at the repo root is one customer skill. Install path: `npx skills add getcargohq/gtm-skills/<name>`. ## 1. Pick the kind | Kind | Frontmatter | Job | | -------- | --------------------------- | -------------------------------------------------------------------------- | | one-off | `metadata.source: one-off` | Runs once in a turn: exact `cargo-ai` command and a price | | pipeline | `metadata.source: cookbook` | Deploys a standing play. Customer copy says **pipeline**, never "cookbook" | `metadata.source` must be exactly one of those two strings. The validator uses `cookbook` as the internal discriminator. Do not put that word in README, plugin listings, skill body, or examples. Customers learn: some skills run once; some deploy a pipeline that keeps running. If the same job exists both ways, they compete for prompts. Pin the seam in `evals/routing.jsonl`: one case that must hit the pipeline, one that must hit the one-off sibling. Name the folder after the job, not the vendor. `name` in frontmatter equals the folder name. The slug is what an agent matches before it opens the file, so the kind has to be readable from the name: | Kind | Shape | Examples | | --------- | --------------------- | ------------------------------------------------------ | | one-off | verb + object | `enrich-company-data`, `score-leads`, `build-tam-list` | | pipeline | the standing practice | `tam-building`, `account-scoring`, `crm-enrichment` | | migration | `x-to-cargo` | `clay-to-cargo` | A verb says "do this once." A standing noun says "keep this running." That is how `build-tam-list` and `tam-building` stay apart, and why `score-leads` pairs with `account-scoring` rather than `score-accounts`. Do not give both kinds the same stem. `enrich-crm` would read as a one-off next to `enrich-company-data`. A pipeline that keeps CRM records filled is `crm-enrichment`. Contacts later stay in that same slug. Accounts that are not in a CRM, if they land, are `account-enrichment`. Planned pipelines stay nouns (`contact-sourcing`, `signal-based-tam`, `rep-cockpit`). Do not rename existing skills to force `-ing` vs `-ment`; the kind matters more than the suffix. Inside a cookbook, resource slugs are snake_case: plays, workflows, tools, agents, segments and folders (`score_accounts`, `enrich_crm_contact`, `account_enrichment`, `tam_tier_analyst`, `tam_tier_a`, `crm_enrichment_plays`), the same as the connectors and models the CDK already requires it of. Workers and apps are the exception: the CDK requires kebab-case for them. File names stay kebab-case (`score-accounts.ts`). A slug is the resource's identity in `cargo.state.json`, so renaming one after a deploy creates a new resource and orphans the old one: get it right before the first deploy. A model that extracts a system you hold (a CRM, a warehouse) pulls all the data. Never filter or pick columns in its `config`: extract every record and every column, and narrow in the SQL or the play filter that reads it. A filter in the config is a second place the question is asked, invisible to anyone reading the queries, and changing it means a redeploy and a re-extraction instead of an edited query. A paid search source is the exception, because there the search is the data source rather than a filter on it: `aiArk.fetchCompanies` or a Sales Navigator search bills per returned record, so its query in `config` is what decides what you buy. ## 2. Description (the only text before load) Four parts, in order: 1. The job in one sentence 2. `Triggers:` literal quoted phrases a user would type 3. Proper nouns (connectors, CRM, CDK) 4. `Skip when:` the sibling skill that should win instead Quote any YAML value that contains colon-space. Unquoted, the installer skips the file with a warning nobody reads. ## 3. Write the kind ### One-off Copy the structure of `enrich-company-data/SKILL.md`: - Setup (CLI install, login, session stamp) - Do the job (exact `cargo-ai orchestration action execute` / `execute-batch`) - Example (see below) - What it costs (table of `integration.action` → credits) - Worth knowing / Going further / star ask Slugs and prices are not yours to invent. `validate.ts` asserts every `integrationSlug`/`actionSlug` and every credit number against `getcargohq/cargo-skills` playbooks. Copy from a sibling, then run `node scripts/validate.ts`. Register: `skills.sh.json`, `hooks/skill-loads.sh`, root `README.md` table. ### Pipeline Copy the section order from `tam-building/SKILL.md`. The validator requires these headings: 1. Honest banner: `**State: to-be-approved.**` 2. `## The outcome` 3. `## Example` (see below) 4. `## Put it in your project` (copy the procedure from `tam-building`; each skill carries its own) 5. `## What you will be asked` — derive before ask; more than ~4 asked rows means you skipped lookups 6. `## What you can change` — offer unprompted; every variation has a cost 7. `## What should not change` — name the concrete symptom if violated 8. `## Done when` 9. `## What it costs` 10. `## Composes into` Folder: ``` <name>/ SKILL.md README.md infra/ # or models/, plays/, agents/ — whatever the outcome needs references/ # supporting agent docs; never a nested SKILL.md evals/acceptance.md ``` Isolation: every model and connector the resources import lives in this folder. No relative import may leave it. Two pipeline skills may both carry an `accounts` model; the placing agent rewires to the project's existing one. Resources must earn their deploy: - Do not wrap a single connector action in a tool. That is a one-off. - Do not declare a segment that restates the play's own filter. - Do not add `examples/example.md` until a live install has sanitized audit findings, mappings, a plan summary, and lookup-backed credits. - A folder of CDK with no `SKILL.md` does not belong at the root. Older examples live in history: `git checkout 305cd88 -- <name>`, then write the skill. **Credits.** Pipeline markdown must not contain a numeric credit amount. Fetch live prices at run time (`cargo-ai connection integration get <slug>`). One-offs quote playbook numbers; pipelines do not. **Write the system you write back to.** If the outcome updates a CRM record, the play runs on that CRM extract and matches the CRM record id. Do not sit a native `unifyAccounts` / `ids` map between the play and the write — the run looks successful and nothing lands. **One CRM shape in the file.** HubSpot as the checked example; Salesforce and Attio adapt that file (connector, extractor, record-id, write action, fill-blank guard). No parallel branches. CDK workflow bodies must compile. These fail `cargo-cdk check` / `plan`: `undefined`/`void`, comma / SequenceExpression, `Date#getTime()`. Register: `skills.sh.json` (its stage, at its place in the order: the order is what an agent suggests next), `hooks/skill-loads.sh`, root README pipeline table, `.github/data/approvals.json` with `state: to-be-approved` and empty evidence. ### Both kinds: personas and the example `validate.ts` requires these of every skill; the site renders them before anyone installs. - `metadata.personas`, right after `metadata.source`: 1–3 of `sales-development`, `account-executive`, `revops`, `sales-leadership`, `marketing`, `gtm-engineering`, primary first. - `## Example`, in exactly this shape: ``` ## Example > Find work emails for the 40 people in webinar-attendees.csv. Illustrative output, fictional records: <table or fenced block, about 20 lines at most> <one closing line with the result in numbers> ``` Fictional companies on the `.example` TLD. A one-off's credits must add up from its own cost table; a pipeline quotes none and shows what the deployed play writes. Never a command in it, and never a table row that opens on a backticked `integration.action` cell — the validator reads that as a price. ## 4. Finish ```sh node scripts/build-catalog.mjs node scripts/generate-llms-txt.ts npm run typecheck npm run validate npm run format:check ``` `catalog.json` and `llms.txt` are generated. Edit SKILL.md, then regenerate. Do not claim an outcome for a pipeline skill until it is approved (demo workspace + two implementations in `approvals.json`). The banner in `SKILL.md` must match that file exactly. ## Anti-patterns - A new root skill that is the same job as an existing slug with extra words (`crm-account-enrichment` vs `crm-enrichment`) - A pipeline named like a one-off (`enrich-crm`, `score-accounts`, `build-tam`) or a one-off named like a pipeline (`tam-building` for a list today) - Customer-facing "cookbook" - Hardcoded credits in a pipeline - Stub walkthroughs that restate `SKILL.md` - `PLACEHOLDER` values that survive into a "done" template when the checked example can use real HubSpot (or whatever) property names - Asking the operator for anything a connector or live schema can answer