$npx -y skills add 40RTY-ai/shopify-admin-skills --skill shopify-admin-gift-message-extractionRead-only: extracts gift messages, gift recipients, and gift flags from order custom attributes and notes for fulfillment teams to print on packing slips.
| 1 | ## Purpose |
| 2 | Pulls gift messages, gift-recipient names, and "is_gift" flags from order custom attributes and order notes for orders that are pending or in-progress fulfillment. Produces a single sheet that the fulfillment team can use to print gift cards / inserts and route gift orders correctly. Read-only — no mutations. |
| 3 | |
| 4 | ## Prerequisites |
| 5 | - Authenticated Shopify CLI session: `shopify store auth --store <domain> --scopes read_orders` |
| 6 | - API scopes: `read_orders` |
| 7 | |
| 8 | ## Parameters |
| 9 | |
| 10 | | Parameter | Type | Required | Default | Description | |
| 11 | |-----------|------|----------|---------|-------------| |
| 12 | | store | string | yes | — | Store domain (e.g., mystore.myshopify.com) | |
| 13 | | days_back | integer | no | 7 | Lookback window of orders to scan | |
| 14 | | status | string | no | unfulfilled | Order status filter: `unfulfilled`, `partial`, `any` | |
| 15 | | message_keys | array | no | `["gift_message","gift_note","Gift Message","Gift Note","message","note_to_recipient"]` | Custom attribute keys (any case) that may contain a gift message | |
| 16 | | recipient_keys | array | no | `["gift_recipient","recipient_name","Gift Recipient","To"]` | Custom attribute keys that may contain a recipient name | |
| 17 | | flag_keys | array | no | `["is_gift","gift","Gift Wrap","gift_wrap"]` | Custom attribute keys whose presence/value marks an order as a gift | |
| 18 | | include_order_note | bool | no | true | Also scan the order-level `note` field for free-text gift messages | |
| 19 | | format | string | no | human | Output format: `human` or `json` | |
| 20 | |
| 21 | ## Safety |
| 22 | |
| 23 | > ℹ️ Read-only skill — no mutations are executed. Output may contain personal text written by customers — handle the resulting CSV with the same care as any customer data export. |
| 24 | |
| 25 | ## Detection Rules |
| 26 | |
| 27 | For each order: |
| 28 | |
| 29 | 1. **Custom attributes** — for each `lineItem.customAttributes` and `order.customAttributes`: |
| 30 | - If `key` (case-insensitive) ∈ `message_keys` → capture as `gift_message` |
| 31 | - If `key` ∈ `recipient_keys` → capture as `gift_recipient` |
| 32 | - If `key` ∈ `flag_keys` AND value is truthy (`true`, `yes`, `1`, non-empty string) → set `is_gift: true` |
| 33 | 2. **Order note** — if `include_order_note: true` and `order.note` matches `/gift|recipient|deliver to|message:/i`, capture the note as a `note_hint` |
| 34 | 3. An order qualifies for the report if `is_gift: true` OR `gift_message` was captured OR `gift_recipient` was captured |
| 35 | |
| 36 | ## Workflow Steps |
| 37 | |
| 38 | 1. Compute filter: `created_at:>='<NOW - days_back days>'` and translate `status` → `fulfillment_status:unfulfilled` / `:partial` / no filter |
| 39 | |
| 40 | 2. **OPERATION:** `orders` — query |
| 41 | **Inputs:** `query: <filter>`, `first: 250`, select `name`, `note`, `customAttributes { key, value }`, `lineItems { name, quantity, customAttributes { key, value } }`, `shippingAddress`, `customer { displayName, defaultEmailAddress { emailAddress } }`, pagination cursor |
| 42 | **Expected output:** Candidate orders |
| 43 | |
| 44 | 3. Apply detection rules to each order |
| 45 | |
| 46 | 4. Build report rows — one per qualifying order, including line items so packers know what to wrap |
| 47 | |
| 48 | ## GraphQL Operations |
| 49 | |
| 50 | ```graphql |
| 51 | # orders:query — validated against api_version 2025-01 |
| 52 | query OrdersForGiftExtraction($query: String!, $after: String) { |
| 53 | orders(first: 250, after: $after, query: $query) { |
| 54 | edges { |
| 55 | node { |
| 56 | id |
| 57 | name |
| 58 | createdAt |
| 59 | note |
| 60 | displayFulfillmentStatus |
| 61 | customAttributes { |
| 62 | key |
| 63 | value |
| 64 | } |
| 65 | shippingAddress { |
| 66 | firstName |
| 67 | lastName |
| 68 | address1 |
| 69 | address2 |
| 70 | city |
| 71 | provinceCode |
| 72 | countryCodeV2 |
| 73 | zip |
| 74 | phone |
| 75 | } |
| 76 | customer { |
| 77 | id |
| 78 | displayName |
| 79 | defaultEmailAddress { |
| 80 | emailAddress |
| 81 | } |
| 82 | } |
| 83 | lineItems(first: 50) { |
| 84 | edges { |
| 85 | node { |
| 86 | id |
| 87 | name |
| 88 | quantity |
| 89 | sku |
| 90 | customAttributes { |
| 91 | key |
| 92 | value |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | pageInfo { |
| 100 | hasNextPage |
| 101 | endCursor |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | ``` |
| 106 | |
| 107 | ## Session Tracking |
| 108 | |
| 109 | **Claude MUST emit the following output at each stage. This is mandatory.** |
| 110 | |
| 111 | **On start**, emit: |
| 112 | ``` |
| 113 | ╔══════════════════════════════════════════════╗ |
| 114 | ║ SKILL: Gift Message Extraction ║ |
| 115 | ║ Store: <store domain> ║ |
| 116 | ║ Started: <YYYY-MM-DD HH:MM UTC> ║ |
| 117 | ╚══════════════════════════════════════════════╝ |
| 118 | ``` |
| 119 | |
| 120 | **After each step**, emit: |
| 121 | ``` |
| 122 | [N/TOTAL] <QUERY|MUTATION> <OperationName> |
| 123 | → Params: <brief summary of key inputs> |