返回 Skills
anthropics/knowledge-work-plugins· Apache-2.0 内容可用

crm-maintenance

Keeps HubSpot current without the owner opening it: creates and updates contacts and deals from email and calendar context, logs notes and calls, and flags stale records. The "stop doing data entry" skill. Use when the user asks to update the CRM, log a call, clean up HubSpot, or add context to a deal.

安装

与 skills.sh 相同的 Command / Prompt 安装方式


name: crm-maintenance description: > Keeps HubSpot current without the owner opening it: creates and updates contacts and deals from email and calendar context, logs notes and calls, and flags stale records. The "stop doing data entry" skill. Use when the user asks to update the CRM, log a call, clean up HubSpot, or add context to a deal.

CRM Maintenance

Quick start

Pull context from the referenced email or calendar event, resolve the right HubSpot contact and deal, log the activity, and surface what changed. For a deal cleanup, audit the deal against recent email/calendar activity and propose updates — never apply them without approval.

User: "log this call to the Acme deal"
→ Read the most recent completed calendar event
→ Confirm attendees map to the Acme deal's contacts
→ Write a call activity on the Acme deal
→ Report: "Logged call to Acme Q2 Expansion. [deal link]"

Workflow

  1. Identify intent. Decide which of three paths applies from the user's message and context:

    • Email path — "update my CRM", "add this to the deal", or any reference to an email thread
    • Call path — "log this call", "log the meeting", or any reference to a calendar event
    • Cleanup path — "clean up HubSpot", "is this deal up to date", or any request to audit a specific deal If the intent is ambiguous (e.g. "update HubSpot" with no referenced email/meeting/deal), ask which path before proceeding.
  2. Gather context.

    • Email path: read the thread (subject, participants, last 1–3 messages). Identify the primary external contact.
    • Call path: read the calendar event (title, attendees, time, description). If no event was specified, use the most recent completed meeting in the last 24 hours and confirm with the user before proceeding.
    • Cleanup path: pull the deal (stage, amount, close date, next-step, associated contacts, activities in last 60 days), plus the last 14 days of email threads and calendar events involving the deal's contacts.
  3. Resolve the HubSpot contact and deal. For email/call paths:

    • Search HubSpot contacts by email address. If a contact is missing, create it from email signature or calendar invite data — announce creation in chat before writing.
    • Find the right deal in this order: (a) explicit match if the user named one, (b) the contact's sole open deal, (c) fuzzy match across the contact's open deals against the email subject or meeting title — confirm before writing, (d) ask the user if no match. Never auto-create a deal.
    • For field names, activity types, and association rules, read reference/hubspot-fields.md before writing anything to HubSpot.
    • If deduplication or deal-resolution feels ambiguous, check reference/gotchas.md before proceeding — it covers the most common failure modes.
  4. Execute the action.

  5. Approval gate — every externally visible write. For contact creation and activity logging, announce before writing and surface the result after. For cleanup edits, do not write anything until the user approves the specific changes.

  6. Report what happened. Tell the user what was written and what's pending. Include a HubSpot link to the affected deal when possible. Keep it short.

Approval gates

  • Never delete records. Not contacts, not deals, not activities. If the user asks, say the skill cannot and direct them to HubSpot.
  • Never change deal stage or close a deal without explicit user approval. Even if evidence is strong. Flag and defer.
  • Never create a new deal unprompted. Ask if the right deal can't be resolved.
  • Announce contact creation before writing. One line — lets the user catch typos or duplicates.
  • Side-by-side diffs for cleanup. Show current value and proposed value; wait for approval per item.

Reference

附带文件

reference/cleanup-checklist.md
# Cleanup checklist

The fields the `crm-maintenance` skill checks during a deal cleanup, plus the evidence needed to flag each. This runs when the user says "clean up HubSpot" or "is this deal up to date" scoped to a specific deal.

Work through every item. Present findings as a review list; write only what the user approves.

## 1. Last activity date

**Check:** Is the deal's `hs_lastactivitydate` older than the most recent real interaction (email or meeting) involving its associated contacts?

**Evidence to flag:** An email thread or calendar event in the last 14 days with one of the deal's contacts, newer than `hs_lastactivitydate`.

**Proposed action:** Offer to log the missing activity. Don't mark the deal itself; logging the activity updates `hs_lastactivitydate` automatically.

## 2. Next-step field (`hs_next_step`)

**Check:** Does the current `hs_next_step` still reflect what was agreed in the latest email or meeting?

**Evidence to flag:** The field says "send pricing" and a subsequent email shows pricing was sent. Or the field is blank and a recent meeting explicitly set a next action.

**Proposed action:** Propose a new value (or blank if the step is completed). Show current → proposed side-by-side.

## 3. Deal stage (`dealstage`)

**Check:** Do recent interactions suggest the deal has moved stage — e.g., proposal sent, contract signed, lost to a competitor?

**Evidence to flag:** Explicit language in email/meeting notes like "we're moving forward," "we signed," "we went with [competitor]," or a change in meeting cadence.

**Proposed action:** **Flag only — never change stage automatically.** Surface the evidence and let the user decide.

## 4. Close date (`closedate`)

**Check:** Has the latest email/meeting referenced a revised timeline that conflicts with the current `closedate`?

**Evidence to flag:** Phrases like "pushing to Q3," "we'll sign by end of month," or "delayed until next quarter."

**Proposed action:** Propose an updated date. Show current → proposed.

## 5. Amount (`amount`)

**Check:** Has the latest email/meeting mentioned a different deal size?

**Evidence to flag:** Explicit revised pricing in a pricing email, expanded scope in a meeting, or reduced scope the customer accepted.

**Proposed action:** Propose an updated amount. Show current → proposed.

## 6. Associated contacts

**Check:** Are there people on recent emails or meetings with the deal's contacts who aren't themselves associated to the deal?

**Evidence to flag:** An email CC or meeting attendee whose domain matches an existing deal contact's domain but who isn't on the deal.

**Proposed action:** Propose adding the missing contact(s) to the deal. Create the contact in HubSpot first if they don't exist yet.

## 7. Notes hygiene

**Check:** Are there notes or activities older than 90 days that explicitly contradict the current deal state?

**Evidence to flag:** A note saying "customer is lost" on a deal still in an open stage, or a note with an outdated contract amount.

**Proposed action:** Add a new note clarifying the current state — **never edit or delete** the old note. Append, don't rewrite history.

## Output format

Present findings as a numbered list, each item showing:
- What was flagged
- The evidence (email subject + date, or meeting title + date)
- The proposed action (or "flag only" for stage changes)

Then ask for approval item-by-item or bulk-approve. Write only what's approved.
reference/examples/cleanup-deal.md
# Example: clean up a stale deal

Worked scenario for the cleanup path — user asks the skill to audit one specific deal, skill surfaces findings and waits for approval.

## Setup

- Deal: "Acme Q2 Expansion" — stage "Proposal Sent," amount $18K, close date May 15, next step "send pricing," `hs_lastactivitydate` 22 days ago
- Deal has two associated contacts: Sarah Lin and Ben Rivera
- Last 14 days of email with Sarah include an Apr 18 thread titled "Re: Acme pricing" where Sarah confirmed the pricing and said "we'll sign once our legal review wraps, should be mid-June"
- A calendar event on Apr 20 titled "Acme — legal review walkthrough" included a new person, `maria.chen@acme.com`, who is not in HubSpot and not on the deal
- No email, meeting, or note suggests the deal has moved stage

## User says

> "Clean up the Acme Q2 Expansion deal."

## Skill behavior

**Step 1 — Identify intent.**
Cleanup path, scoped to one named deal.

**Step 2 — Gather context.**
Pulls the deal record. Pulls email threads from the last 14 days involving Sarah and Ben. Pulls calendar events from the same window.

**Step 3 — Resolve.**
Deal explicitly named — no resolution needed. Contacts on recent emails/meetings: Sarah (on deal), Ben (on deal), Maria Chen (not on deal, not in HubSpot).

**Step 4 — Walk the cleanup checklist.**

Skill does NOT write anything yet. Produces this review list:

> **Cleanup review — Acme Q2 Expansion**
>
> **1. Last activity date.** Deal shows 22 days old. Newer activity found: Apr 18 email from Sarah ("Re: Acme pricing"), Apr 20 meeting ("Acme — legal review walkthrough"). Neither is logged to HubSpot.
> **→ Propose:** Log the Apr 18 email and the Apr 20 meeting as activities on this deal.
>
> **2. Next-step field.** Currently "send pricing." The Apr 18 email shows pricing was sent and confirmed by Sarah.
> **→ Propose:** `hs_next_step`: "send pricing" → "wait for legal review sign-off."
>
> **3. Deal stage.** Currently "Proposal Sent." No evidence of stage movement — Sarah said they'll sign *after* legal review. Stage is consistent with reality.
> **→ Flag only — no change.**
>
> **4. Close date.** Currently May 15. Sarah's Apr 18 email says "should be mid-June."
> **→ Propose:** `closedate`: May 15 → June 15.
>
> **5. Amount.** Currently $18K. Apr 18 email confirms 50 seats at $360 = $18K. No change needed.
> **→ No action.**
>
> **6. Associated contacts.** Maria Chen (`maria.chen@acme.com`) attended the Apr 20 legal review meeting. Not on the deal, not in HubSpot.
> **→ Propose:** Create the contact and associate to the deal.
>
> **7. Notes hygiene.** No conflicting notes found in the last 90 days.
> **→ No action.**
>
> Approve items 1, 2, 4, 6 to write? Or specify which ones.

**Step 5 — Approval gate.**
Waits for the user. User replies: *"Yes to all except 4 — leave close date alone, she might be optimistic."*

**Step 6 — Execute approved items.**
- Logs the Apr 18 email as an email activity
- Logs the Apr 20 meeting as a call activity with attendees Sarah, Ben, and (newly created) Maria
- Updates `hs_next_step` to "wait for legal review sign-off"
- Does NOT update `closedate`
- Creates Maria Chen contact and associates to the deal

**Report.**

> Wrote changes to Acme Q2 Expansion:
> - Logged Apr 18 email and Apr 20 call activities
> - Updated next step to "wait for legal review sign-off"
> - Created contact Maria Chen (maria.chen@acme.com) and added to deal
> Close date unchanged per your call.
> Deal stage was flagged as consistent with reality — no change needed.
> [HubSpot: Acme Q2 Expansion]

## What this example demonstrates

- Every field check from `reference/cleanup-checklist.md` applied to one deal
- Side-by-side current → proposed framing for every proposed change
- Stage change correctly held as flag-only
- User selectively approves items — skill honors the exact selection
- Contact creation folded into the same approval cycle
reference/examples/log-call-happy-path.md
# Example: log a call with a missing contact

Worked scenario for the call path — calendar event has three attendees, one of them isn't in HubSpot yet.

## Setup

- User just finished a 30-minute Google Meet titled "Acme — technical deep dive" at 10:00am
- Attendees on the calendar invite: the user, `sarah.lin@acme.com`, `ben.rivera@acme.com`
- Sarah Lin is an existing HubSpot contact on the "Acme Q2 Expansion" deal
- Ben Rivera is NOT in HubSpot
- The Acme Q2 Expansion deal is the only open deal for Sarah

## User says

> "Log this call."

## Skill behavior

**Step 1 — Identify intent.**
Call path. No specific event named — falls back to the most recent completed meeting in the last 24 hours.

**Step 2 — Gather context.**
Finds the "Acme — technical deep dive" event from 10:00am. Confirms with the user before proceeding:

> *"Logging the 'Acme — technical deep dive' call from 10:00am this morning — correct?"*

User confirms.

Reads event details: 30 minutes, external attendees `sarah.lin@acme.com` and `ben.rivera@acme.com`. No description on the event.

**Step 3 — Resolve contact + deal.**
- Looks up `sarah.lin@acme.com` → exists (Sarah Lin, on Acme Q2 Expansion).
- Looks up `ben.rivera@acme.com` → does not exist.
- Announces contact creation:

> *"Ben Rivera (ben.rivera@acme.com) isn't in HubSpot. Creating the contact and associating to the Acme Q2 Expansion deal."*

Creates the contact with `email`, `firstname: Ben`, `lastname: Rivera`, `company: Acme` (inferred from email domain). Associates to the deal.

Deal resolution: Sarah's only open deal is Acme Q2 Expansion — unambiguous match.

**Step 4 — Execute.**
Writes a call activity on Acme Q2 Expansion:
- `hs_call_title`: Acme — technical deep dive
- `hs_call_body`: "Meeting held; notes to be added." (No description on the calendar event; placeholder surfaced to user so they know to add detail.)
- `hs_call_duration`: 30 min
- `hs_timestamp`: 10:00am today
- Associated contacts: Sarah Lin, Ben Rivera
- Associated deal: Acme Q2 Expansion

**Step 5 — Approval gate.**
Announced the contact creation before writing. Activity write is inline per the user's explicit "log this call" request.

**Step 6 — Report.**

> Logged call activity to Acme Q2 Expansion:
> *"Acme — technical deep dive" (30 min, 10:00am)*
> Created new contact: Ben Rivera (ben.rivera@acme.com) — associated to the deal.
> Call body is a placeholder — add your notes if you want them on the deal timeline.
> [HubSpot: Acme Q2 Expansion]

## What this example demonstrates

- Fallback to most-recent-meeting when the user doesn't specify an event
- Confirmation before acting on inferred context
- Contact creation with inferred company from email domain
- Placeholder body with user notification when no meeting notes exist
- Associating multiple contacts (existing + newly created) to the same activity
reference/examples/log-email-happy-path.md
# Example: log email to an existing deal (happy path)

A worked scenario for the email path — contact exists, deal resolves unambiguously, activity logs cleanly.

## Setup

- User is viewing an email thread with subject "Acme Q2 pricing follow-up"
- Thread has three messages between the user and `sarah.lin@acme.com` over the last two days
- `sarah.lin@acme.com` is an existing contact in HubSpot
- She is associated to exactly one open deal: "Acme Q2 Expansion" (proposal sent, $18K, close date May 15)

## User says

> "Add this thread to the deal."

## Skill behavior

**Step 1 — Identify intent.**
Email path (user said "this thread").

**Step 2 — Gather context.**
Reads the thread. Subject: "Acme Q2 pricing follow-up." Latest message (yesterday, 4:12pm) from Sarah: *"Confirmed, 50 seats at $360/seat works. Sending the signed SOW Friday."* Primary external contact: `sarah.lin@acme.com`.

**Step 3 — Resolve contact + deal.**
Looks up `sarah.lin@acme.com` → contact exists (Sarah Lin, Acme Corp). Finds open deals associated to her: exactly one, "Acme Q2 Expansion." Match is unambiguous.

**Step 4 — Execute.**
Writes an email activity on Acme Q2 Expansion:
- `hs_email_subject`: Acme Q2 pricing follow-up
- `hs_email_text`: "Sarah confirmed 50 seats at $360/seat ($18K). Signed SOW coming Friday."
- `hs_timestamp`: yesterday, 4:12pm
- Associated contact: Sarah Lin
- Associated deal: Acme Q2 Expansion

**Step 5 — Approval gate.**
Contact already existed, no creation announcement needed. Activity write happens inline — no separate gate because the user explicitly asked.

**Step 6 — Report.**

> Logged email activity to Acme Q2 Expansion:
> *"Sarah confirmed 50 seats at $360/seat ($18K). Signed SOW coming Friday."*
> Timestamped yesterday 4:12pm. No contact created — Sarah was already on the deal.
> [HubSpot: Acme Q2 Expansion]

## What makes this the happy path

- Single unambiguous deal association — no fuzzy match needed
- Contact already existed — no creation flow
- Thread subject is descriptive — usable as-is for activity title
- Summary focuses on decisions and numbers, not full thread transcript
reference/gotchas.md
# Gotchas

Edge cases where the `crm-maintenance` skill is most likely to produce bad CRM data. Each entry uses the Good / Bad pattern.

---

## Gotcha: Creating a duplicate contact when the email is already in HubSpot under a different case

**Why it matters:** HubSpot dedupes on exact email match. `Sarah.Lin@acme.com` and `sarah.lin@acme.com` are treated as distinct, but an owner who already created one will see a silent duplicate appear. That destroys the owner's trust in the skill.

### ✗ Bad

> Searched HubSpot for `Sarah.Lin@acme.com` — no match. Creating new contact: Sarah Lin (Sarah.Lin@acme.com).

New contact is created; owner's existing `sarah.lin@acme.com` contact now has a duplicate.

### ✓ Good

> Searched HubSpot for `sarah.lin@acme.com` (case-insensitive) — no match. Creating new contact: Sarah Lin (sarah.lin@acme.com). Announcing before write.

Always normalize email to lowercase before lookup. Announce the creation so the owner can catch misses.

---

## Gotcha: Writing the full email thread as the activity body

**Why it matters:** HubSpot activities are signal surfaces for fast scanning, not a transcript archive. A 12-message thread pasted into the activity body is useless; a three-sentence summary of what was agreed is actionable.

### ✗ Bad

> **Activity body:**
> *From: Sarah Lin — Thu Apr 9…*
> *Hi! Following up on our call, here are the specs we discussed…*
> *[1,200 more words]*

Owner scrolls past it every time.

### ✓ Good

> **Activity body:** Sarah confirmed scope for Q2 expansion — 50 seats, $18K ACV, start date June 1. She'll send the signed SOW by Friday. No open questions.

Summary names the decisions, the numbers, and the next step.

---

## Gotcha: Auto-creating a deal when the right one can't be resolved

**Why it matters:** Duplicate deals are very hard to untangle in HubSpot — they split activity history, confuse reporting, and the owner usually notices weeks later when a forecast looks wrong.

### ✗ Bad

> No open deal found for Sarah Lin at Acme. Creating new deal: "Acme — new opportunity" and logging the call to it.

Now there are two Acme deals and the owner has no idea which one is real.

### ✓ Good

> No open deal found for Sarah Lin at Acme. Which deal should I log this to? Options: (1) Acme Q2 Expansion (closed-won Feb), (2) Acme Renewal 2027 (open). Or do you want to skip logging for now?

Ask. Never create a deal on behalf of the owner.

---

## Gotcha: Proposing a deal stage change based on email language

**Why it matters:** "We're moving forward" in an email doesn't always mean "advance to closed-won." Customers say it when they're moving forward with evaluation, or with a procurement step. Owners have context the skill doesn't. Auto-advancing stage is destructive to the pipeline.

### ✗ Bad

> Email from Sarah says "we're moving forward." Proposing `dealstage` → Closed Won. Writing now.

### ✓ Good

> Flagging: the Apr 18 email from Sarah ("we're moving forward with the Anthropic evaluation") may indicate stage movement. Current stage is "Proposal Sent." Do you want to advance it? I won't change stage without your explicit go-ahead.

Surface the evidence, hold the write.

---

## Gotcha: Overwriting an owner-set next-step during cleanup

**Why it matters:** The owner may have set a next step that reflects context the skill can't see — a phone call, a Slack thread, a private note. Overwriting it because a recent email suggests something different erases real work.

### ✗ Bad

> `hs_next_step` currently says "wait for legal review." Latest email from Sarah says "let's schedule pricing review." Updating `hs_next_step` to "schedule pricing review."

### ✓ Good

> `hs_next_step` currently reads "wait for legal review" (set by you 3 days ago). The Apr 19 email from Sarah suggests a pricing review is also needed. Propose appending or replacing? Current → Proposed: "wait for legal review" → "wait for legal review; then schedule pricing review." Approve?

Show the current value, propose the change explicitly, wait for approval.
reference/hubspot-fields.md
# HubSpot fields and activity types

The specific HubSpot objects, properties, and activity types the `crm-maintenance` skill reads from and writes to. Only the fields listed here are in-scope for this skill — everything else in HubSpot is untouched.

## Contacts — write

| Field         | Usage                                                                                  |
|---------------|----------------------------------------------------------------------------------------|
| `email`       | Primary identifier for lookup + dedupe. Always set on creation.                        |
| `firstname`   | Set from email signature or calendar invite if available. Leave blank if unknown.      |
| `lastname`    | Set from email signature or calendar invite if available. Leave blank if unknown.      |
| `company`     | Set from email signature domain or calendar organization if available.                 |

Do not write any other contact properties. Owner, lifecycle stage, and lead source are user-managed fields — never overwrite.

## Contacts — read (for lookup)

| Field         | Usage                                                              |
|---------------|--------------------------------------------------------------------|
| `email`       | Search key. Case-insensitive exact match.                          |
| `firstname` · `lastname` · `company` | Displayed to the user during ambiguity resolution. |
| `hs_object_id`| Used for association with deals and activities.                    |

## Deals — read (all cleanup + resolution paths)

| Field                       | Usage                                                    |
|-----------------------------|----------------------------------------------------------|
| `dealname`                  | Displayed to the user; used for fuzzy match against email/meeting topic |
| `dealstage`                 | Read-only during cleanup — flag discrepancies, never change |
| `amount`                    | Read during cleanup; flag if recent email/meeting implies change |
| `closedate`                 | Read during cleanup; flag if outdated                    |
| `hs_next_step`              | Read + propose updates during cleanup                    |
| `hubspot_owner_id`          | Displayed to the user; never changed                     |
| `hs_lastactivitydate`       | Used to detect stale deals                               |
| Associated contacts         | Used to determine whether recent email/meeting participants are on the deal |

## Deals — write (cleanup path only, with approval)

| Field           | Rule                                                             |
|-----------------|------------------------------------------------------------------|
| `hs_next_step`  | Propose updates; write only with explicit user approval          |
| `closedate`     | Propose updates; write only with explicit user approval          |
| `amount`        | Propose updates; write only with explicit user approval          |
| Contact assoc.  | Propose adding missing deal participants; write only with approval |

**Never write** `dealstage`, `pipeline`, `hubspot_owner_id`, or any custom property during cleanup. Those are owner-managed.

## Activities — write

| Activity type                  | Used by        | Fields set                                                               |
|--------------------------------|----------------|--------------------------------------------------------------------------|
| Email engagement (`EMAIL`)     | Email path     | `hs_email_subject` (thread subject), `hs_email_text` (summary, not full thread), `hs_timestamp` (latest message time), associated contact(s) + deal |
| Call engagement (`CALL`)       | Call path      | `hs_call_title` (event title), `hs_call_body` (summary), `hs_call_duration` (from calendar), `hs_timestamp` (event start), associated contact(s) + deal |
| Note (`NOTE`)                  | Cleanup path   | `hs_note_body` (when flagging something for future review that doesn't fit a field update) |

Use HubSpot's standard engagement vocabulary. Do not invent custom activity types.

## Association rules

- Every activity must associate to the deal AND to at least one contact.
- If a contact is created on-the-fly during activity logging, associate it to the deal in the same operation so the activity shows up on both the contact and deal timelines.
- Do not associate a contact to a deal during an activity-logging flow unless the contact is actually a participant in that email thread or meeting.