Back to skills
SkillHub ClubShip Full StackFull StackBackendIntegration

shopify

This skill provides a CLI-based project initializer for Shopify development, covering apps, extensions, and themes. It generates configuration files, sets up basic project structure, and guides users through Shopify CLI commands. The tool handles OAuth scopes, extension types, and provides practical next-step instructions for developers.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
1,862
Hot score
99
Updated
March 20, 2026
Overall rating
A7.7
Composite score
6.3
Best-practice grade
B75.6

Install command

npx @skill-hub/cli install mrgoonie-claudekit-skills-shopify
shopifye-commerceapi-integrationcli-toolweb-development

Repository

mrgoonie/claudekit-skills

Skill path: .claude/skills/shopify

This skill provides a CLI-based project initializer for Shopify development, covering apps, extensions, and themes. It generates configuration files, sets up basic project structure, and guides users through Shopify CLI commands. The tool handles OAuth scopes, extension types, and provides practical next-step instructions for developers.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Backend, Integration.

Target audience: Shopify developers and agencies building custom storefronts, checkout extensions, or admin tools who need quick project scaffolding.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: mrgoonie.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install shopify into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/mrgoonie/claudekit-skills before adding shopify to shared team environments
  • Use shopify for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 1.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: shopify
description: Build Shopify applications, extensions, and themes using GraphQL/REST APIs, Shopify CLI, Polaris UI components, and Liquid templating. Capabilities include app development with OAuth authentication, checkout UI extensions for customizing checkout flow, admin UI extensions for dashboard integration, POS extensions for retail, theme development with Liquid, webhook management, billing API integration, product/order/customer management. Use when building Shopify apps, implementing checkout customizations, creating admin interfaces, developing themes, integrating payment processing, managing store data via APIs, or extending Shopify functionality.
---

# Shopify Development

Comprehensive guide for building on Shopify platform: apps, extensions, themes, and API integrations.

## Platform Overview

**Core Components:**
- **Shopify CLI** - Development workflow tool
- **GraphQL Admin API** - Primary API for data operations (recommended)
- **REST Admin API** - Legacy API (maintenance mode)
- **Polaris UI** - Design system for consistent interfaces
- **Liquid** - Template language for themes

**Extension Points:**
- Checkout UI - Customize checkout experience
- Admin UI - Extend admin dashboard
- POS UI - Point of Sale customization
- Customer Account - Post-purchase pages
- Theme App Extensions - Embedded theme functionality

## Quick Start

### Prerequisites

```bash
# Install Shopify CLI
npm install -g @shopify/cli@latest

# Verify installation
shopify version
```

### Create New App

```bash
# Initialize app
shopify app init

# Start development server
shopify app dev

# Generate extension
shopify app generate extension --type checkout_ui_extension

# Deploy
shopify app deploy
```

### Theme Development

```bash
# Initialize theme
shopify theme init

# Start local preview
shopify theme dev

# Pull from store
shopify theme pull --live

# Push to store
shopify theme push --development
```

## Development Workflow

### 1. App Development

**Setup:**
```bash
shopify app init
cd my-app
```

**Configure Access Scopes** (`shopify.app.toml`):
```toml
[access_scopes]
scopes = "read_products,write_products,read_orders"
```

**Start Development:**
```bash
shopify app dev  # Starts local server with tunnel
```

**Add Extensions:**
```bash
shopify app generate extension --type checkout_ui_extension
```

**Deploy:**
```bash
shopify app deploy  # Builds and uploads to Shopify
```

### 2. Extension Development

**Available Types:**
- Checkout UI - `checkout_ui_extension`
- Admin Action - `admin_action`
- Admin Block - `admin_block`
- POS UI - `pos_ui_extension`
- Function - `function` (discounts, payment, delivery, validation)

**Workflow:**
```bash
shopify app generate extension
# Select type, configure
shopify app dev  # Test locally
shopify app deploy  # Publish
```

### 3. Theme Development

**Setup:**
```bash
shopify theme init
# Choose Dawn (reference theme) or start fresh
```

**Local Development:**
```bash
shopify theme dev
# Preview at localhost:9292
# Auto-syncs to development theme
```

**Deployment:**
```bash
shopify theme push --development  # Push to dev theme
shopify theme publish --theme=123  # Set as live
```

## When to Build What

### Build an App When:
- Integrating external services
- Adding functionality across multiple stores
- Building merchant-facing admin tools
- Managing store data programmatically
- Implementing complex business logic
- Charging for functionality

### Build an Extension When:
- Customizing checkout flow
- Adding fields/features to admin pages
- Creating POS actions for retail
- Implementing discount/payment/shipping rules
- Extending customer account pages

### Build a Theme When:
- Creating custom storefront design
- Building unique shopping experiences
- Customizing product/collection pages
- Implementing brand-specific layouts
- Modifying homepage/content pages

### Combination Approach:
**App + Theme Extension:**
- App handles backend logic and data
- Theme extension provides storefront UI
- Example: Product reviews, wishlists, size guides

## Essential Patterns

### GraphQL Product Query

```graphql
query GetProducts($first: Int!) {
  products(first: $first) {
    edges {
      node {
        id
        title
        handle
        variants(first: 5) {
          edges {
            node {
              id
              price
              inventoryQuantity
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

### Checkout Extension (React)

```javascript
import { reactExtension, BlockStack, TextField, Checkbox } from '@shopify/ui-extensions-react/checkout';

export default reactExtension('purchase.checkout.block.render', () => <Extension />);

function Extension() {
  const [message, setMessage] = useState('');

  return (
    <BlockStack>
      <TextField label="Gift Message" value={message} onChange={setMessage} />
    </BlockStack>
  );
}
```

### Liquid Product Display

```liquid
{% for product in collection.products %}
  <div class="product-card">
    <img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
    <h3>{{ product.title }}</h3>
    <p>{{ product.price | money }}</p>
    <a href="{{ product.url }}">View Details</a>
  </div>
{% endfor %}
```

## Best Practices

**API Usage:**
- Prefer GraphQL over REST for new development
- Request only needed fields to reduce costs
- Implement pagination for large datasets
- Use bulk operations for batch processing
- Respect rate limits (cost-based for GraphQL)

**Security:**
- Store API credentials in environment variables
- Verify webhook signatures
- Use OAuth for public apps
- Request minimal access scopes
- Implement session tokens for embedded apps

**Performance:**
- Cache API responses when appropriate
- Optimize images in themes
- Minimize Liquid logic complexity
- Use async loading for extensions
- Monitor query costs in GraphQL

**Testing:**
- Use development stores for testing
- Test across different store plans
- Verify mobile responsiveness
- Check accessibility (keyboard, screen readers)
- Validate GDPR compliance

## Reference Documentation

Detailed guides for advanced topics:

- **[App Development](references/app-development.md)** - OAuth, APIs, webhooks, billing
- **[Extensions](references/extensions.md)** - Checkout, Admin, POS, Functions
- **[Themes](references/themes.md)** - Liquid, sections, deployment

## Scripts

**[shopify_init.py](scripts/shopify_init.py)** - Initialize Shopify projects interactively
```bash
python scripts/shopify_init.py
```

## Troubleshooting

**Rate Limit Errors:**
- Monitor `X-Shopify-Shop-Api-Call-Limit` header
- Implement exponential backoff
- Use bulk operations for large datasets

**Authentication Failures:**
- Verify access token validity
- Check required scopes granted
- Ensure OAuth flow completed

**Extension Not Appearing:**
- Verify extension target correct
- Check extension published
- Ensure app installed on store

**Webhook Not Receiving:**
- Verify webhook URL accessible
- Check signature validation
- Review logs in Partner Dashboard

## Resources

**Official Documentation:**
- Shopify Docs: https://shopify.dev/docs
- GraphQL API: https://shopify.dev/docs/api/admin-graphql
- Shopify CLI: https://shopify.dev/docs/api/shopify-cli
- Polaris: https://polaris.shopify.com

**Tools:**
- GraphiQL Explorer (Admin → Settings → Apps → Develop apps)
- Partner Dashboard (app management)
- Development stores (free testing)

**API Versioning:**
- Quarterly releases (YYYY-MM format)
- Current: 2025-01
- 12-month support per version
- Test before version updates

---

**Note:** This skill covers Shopify platform as of January 2025. Refer to official documentation for latest updates.


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/app-development.md

```markdown
# App Development Reference

Guide for building Shopify apps with OAuth, GraphQL/REST APIs, webhooks, and billing.

## OAuth Authentication

### OAuth 2.0 Flow

**1. Redirect to Authorization URL:**
```
https://{shop}.myshopify.com/admin/oauth/authorize?
  client_id={api_key}&
  scope={scopes}&
  redirect_uri={redirect_uri}&
  state={nonce}
```

**2. Handle Callback:**
```javascript
app.get('/auth/callback', async (req, res) => {
  const { code, shop, state } = req.query;

  // Verify state to prevent CSRF
  if (state !== storedState) {
    return res.status(403).send('Invalid state');
  }

  // Exchange code for access token
  const accessToken = await exchangeCodeForToken(shop, code);

  // Store token securely
  await storeAccessToken(shop, accessToken);

  res.redirect(`https://${shop}/admin/apps/${appHandle}`);
});
```

**3. Exchange Code for Token:**
```javascript
async function exchangeCodeForToken(shop, code) {
  const response = await fetch(`https://${shop}/admin/oauth/access_token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: process.env.SHOPIFY_API_KEY,
      client_secret: process.env.SHOPIFY_API_SECRET,
      code
    })
  });

  const { access_token } = await response.json();
  return access_token;
}
```

### Access Scopes

**Common Scopes:**
- `read_products`, `write_products` - Product catalog
- `read_orders`, `write_orders` - Order management
- `read_customers`, `write_customers` - Customer data
- `read_inventory`, `write_inventory` - Stock levels
- `read_fulfillments`, `write_fulfillments` - Order fulfillment
- `read_shipping`, `write_shipping` - Shipping rates
- `read_analytics` - Store analytics
- `read_checkouts`, `write_checkouts` - Checkout data

Full list: https://shopify.dev/api/usage/access-scopes

### Session Tokens (Embedded Apps)

For embedded apps using App Bridge:

```javascript
import { getSessionToken } from '@shopify/app-bridge/utilities';

async function authenticatedFetch(url, options = {}) {
  const app = createApp({ ... });
  const token = await getSessionToken(app);

  return fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${token}`
    }
  });
}
```

## GraphQL Admin API

### Making Requests

```javascript
async function graphqlRequest(shop, accessToken, query, variables = {}) {
  const response = await fetch(
    `https://${shop}/admin/api/2025-01/graphql.json`,
    {
      method: 'POST',
      headers: {
        'X-Shopify-Access-Token': accessToken,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ query, variables })
    }
  );

  const data = await response.json();

  if (data.errors) {
    throw new Error(`GraphQL errors: ${JSON.stringify(data.errors)}`);
  }

  return data.data;
}
```

### Product Operations

**Create Product:**
```graphql
mutation CreateProduct($input: ProductInput!) {
  productCreate(input: $input) {
    product {
      id
      title
      handle
    }
    userErrors {
      field
      message
    }
  }
}
```

Variables:
```json
{
  "input": {
    "title": "New Product",
    "productType": "Apparel",
    "vendor": "Brand",
    "status": "ACTIVE",
    "variants": [
      { "price": "29.99", "sku": "SKU-001", "inventoryQuantity": 100 }
    ]
  }
}
```

**Update Product:**
```graphql
mutation UpdateProduct($input: ProductInput!) {
  productUpdate(input: $input) {
    product { id title }
    userErrors { field message }
  }
}
```

**Query Products:**
```graphql
query GetProducts($first: Int!, $query: String) {
  products(first: $first, query: $query) {
    edges {
      node {
        id
        title
        status
        variants(first: 5) {
          edges {
            node { id price inventoryQuantity }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

### Order Operations

**Query Orders:**
```graphql
query GetOrders($first: Int!) {
  orders(first: $first) {
    edges {
      node {
        id
        name
        createdAt
        displayFinancialStatus
        totalPriceSet {
          shopMoney { amount currencyCode }
        }
        customer { email firstName lastName }
      }
    }
  }
}
```

**Fulfill Order:**
```graphql
mutation FulfillOrder($input: FulfillmentInput!) {
  fulfillmentCreate(input: $input) {
    fulfillment { id status trackingInfo { number url } }
    userErrors { field message }
  }
}
```

## Webhooks

### Configuration

In `shopify.app.toml`:
```toml
[webhooks]
api_version = "2025-01"

[[webhooks.subscriptions]]
topics = ["orders/create"]
uri = "/webhooks/orders/create"

[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "/webhooks/products/update"

[[webhooks.subscriptions]]
topics = ["app/uninstalled"]
uri = "/webhooks/app/uninstalled"

# GDPR mandatory webhooks
[webhooks.privacy_compliance]
customer_data_request_url = "/webhooks/gdpr/data-request"
customer_deletion_url = "/webhooks/gdpr/customer-deletion"
shop_deletion_url = "/webhooks/gdpr/shop-deletion"
```

### Webhook Handler

```javascript
import crypto from 'crypto';

function verifyWebhook(req) {
  const hmac = req.headers['x-shopify-hmac-sha256'];
  const body = req.rawBody; // Raw body buffer

  const hash = crypto
    .createHmac('sha256', process.env.SHOPIFY_API_SECRET)
    .update(body, 'utf8')
    .digest('base64');

  return hmac === hash;
}

app.post('/webhooks/orders/create', async (req, res) => {
  if (!verifyWebhook(req)) {
    return res.status(401).send('Unauthorized');
  }

  const order = req.body;
  console.log('New order:', order.id, order.name);

  // Process order...

  res.status(200).send('OK');
});
```

### Common Webhook Topics

**Orders:**
- `orders/create`, `orders/updated`, `orders/delete`
- `orders/paid`, `orders/cancelled`, `orders/fulfilled`

**Products:**
- `products/create`, `products/update`, `products/delete`

**Customers:**
- `customers/create`, `customers/update`, `customers/delete`

**Inventory:**
- `inventory_levels/update`

**App:**
- `app/uninstalled` (critical for cleanup)

## Billing Integration

### App Charges

**One-time Charge:**
```graphql
mutation CreateCharge($input: AppPurchaseOneTimeInput!) {
  appPurchaseOneTimeCreate(input: $input) {
    appPurchaseOneTime {
      id
      name
      price { amount }
      status
      confirmationUrl
    }
    userErrors { field message }
  }
}
```

Variables:
```json
{
  "input": {
    "name": "Premium Feature",
    "price": { "amount": 49.99, "currencyCode": "USD" },
    "returnUrl": "https://your-app.com/billing/callback"
  }
}
```

**Recurring Charge (Subscription):**
```graphql
mutation CreateSubscription($input: AppSubscriptionCreateInput!) {
  appSubscriptionCreate(input: $input) {
    appSubscription {
      id
      name
      status
      confirmationUrl
    }
    userErrors { field message }
  }
}
```

Variables:
```json
{
  "input": {
    "name": "Monthly Subscription",
    "returnUrl": "https://your-app.com/billing/callback",
    "lineItems": [
      {
        "plan": {
          "appRecurringPricingDetails": {
            "price": { "amount": 29.99, "currencyCode": "USD" },
            "interval": "EVERY_30_DAYS"
          }
        }
      }
    ]
  }
}
```

**Usage-based Billing:**
```graphql
mutation CreateUsageCharge($input: AppUsageRecordCreateInput!) {
  appUsageRecordCreate(input: $input) {
    appUsageRecord {
      id
      price { amount }
      description
    }
    userErrors { field message }
  }
}
```

## Metafields

### Create Metafield

```graphql
mutation CreateMetafield($input: MetafieldInput!) {
  metafieldsSet(metafields: [$input]) {
    metafields {
      id
      namespace
      key
      value
    }
    userErrors { field message }
  }
}
```

Variables:
```json
{
  "input": {
    "ownerId": "gid://shopify/Product/123",
    "namespace": "custom",
    "key": "instructions",
    "value": "Handle with care",
    "type": "single_line_text_field"
  }
}
```

**Metafield Types:**
- `single_line_text_field`, `multi_line_text_field`
- `number_integer`, `number_decimal`
- `date`, `date_time`
- `url`, `json`
- `file_reference`, `product_reference`

## Rate Limiting

### GraphQL Cost-Based Limits

**Limits:**
- Available points: 2000
- Restore rate: 100 points/second
- Max query cost: 2000

**Check Cost:**
```javascript
const response = await graphqlRequest(shop, token, query);
const cost = response.extensions?.cost;

console.log(`Cost: ${cost.actualQueryCost}/${cost.throttleStatus.maximumAvailable}`);
```

**Handle Throttling:**
```javascript
async function graphqlWithRetry(shop, token, query, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await graphqlRequest(shop, token, query);
    } catch (error) {
      if (error.message.includes('Throttled') && i < retries - 1) {
        await sleep(Math.pow(2, i) * 1000); // Exponential backoff
        continue;
      }
      throw error;
    }
  }
}
```

## Best Practices

**Security:**
- Store credentials in environment variables
- Verify webhook HMAC signatures
- Validate OAuth state parameter
- Use HTTPS for all endpoints
- Implement rate limiting on your endpoints

**Performance:**
- Cache access tokens securely
- Use bulk operations for large datasets
- Implement pagination for queries
- Monitor GraphQL query costs

**Reliability:**
- Implement exponential backoff for retries
- Handle webhook delivery failures
- Log errors for debugging
- Monitor app health metrics

**Compliance:**
- Implement GDPR webhooks (mandatory)
- Handle customer data deletion requests
- Provide data export functionality
- Follow data retention policies

```

### references/extensions.md

```markdown
# Extensions Reference

Guide for building UI extensions and Shopify Functions.

## Checkout UI Extensions

Customize checkout and thank-you pages with native-rendered components.

### Extension Points

**Block Targets (Merchant-Configurable):**
- `purchase.checkout.block.render` - Main checkout
- `purchase.thank-you.block.render` - Thank you page

**Static Targets (Fixed Position):**
- `purchase.checkout.header.render-after`
- `purchase.checkout.contact.render-before`
- `purchase.checkout.shipping-option-list.render-after`
- `purchase.checkout.payment-method-list.render-after`
- `purchase.checkout.footer.render-before`

### Setup

```bash
shopify app generate extension --type checkout_ui_extension
```

Configuration (`shopify.extension.toml`):
```toml
api_version = "2025-01"
name = "gift-message"
type = "ui_extension"

[[extensions.targeting]]
target = "purchase.checkout.block.render"

[capabilities]
network_access = true
api_access = true
```

### Basic Example

```javascript
import { reactExtension, BlockStack, TextField, Checkbox, useApi } from '@shopify/ui-extensions-react/checkout';

export default reactExtension('purchase.checkout.block.render', () => <Extension />);

function Extension() {
  const [message, setMessage] = useState('');
  const [isGift, setIsGift] = useState(false);
  const { applyAttributeChange } = useApi();

  useEffect(() => {
    if (isGift) {
      applyAttributeChange({
        type: 'updateAttribute',
        key: 'gift_message',
        value: message
      });
    }
  }, [message, isGift]);

  return (
    <BlockStack spacing="loose">
      <Checkbox checked={isGift} onChange={setIsGift}>
        This is a gift
      </Checkbox>
      {isGift && (
        <TextField
          label="Gift Message"
          value={message}
          onChange={setMessage}
          multiline={3}
        />
      )}
    </BlockStack>
  );
}
```

### Common Hooks

**useApi:**
```javascript
const { extensionPoint, shop, storefront, i18n, sessionToken } = useApi();
```

**useCartLines:**
```javascript
const lines = useCartLines();
lines.forEach(line => {
  console.log(line.merchandise.product.title, line.quantity);
});
```

**useShippingAddress:**
```javascript
const address = useShippingAddress();
console.log(address.city, address.countryCode);
```

**useApplyCartLinesChange:**
```javascript
const applyChange = useApplyCartLinesChange();

async function addItem() {
  await applyChange({
    type: 'addCartLine',
    merchandiseId: 'gid://shopify/ProductVariant/123',
    quantity: 1
  });
}
```

### Core Components

**Layout:**
- `BlockStack` - Vertical stacking
- `InlineStack` - Horizontal layout
- `Grid`, `GridItem` - Grid layout
- `View` - Container
- `Divider` - Separator

**Input:**
- `TextField` - Text input
- `Checkbox` - Boolean
- `Select` - Dropdown
- `DatePicker` - Date selection
- `Form` - Form wrapper

**Display:**
- `Text`, `Heading` - Typography
- `Banner` - Messages
- `Badge` - Status
- `Image` - Images
- `Link` - Hyperlinks
- `List`, `ListItem` - Lists

**Interactive:**
- `Button` - Actions
- `Modal` - Overlays
- `Pressable` - Click areas

## Admin UI Extensions

Extend Shopify admin interface.

### Admin Action

Custom actions on resource pages.

```bash
shopify app generate extension --type admin_action
```

```javascript
import { reactExtension, AdminAction, Button } from '@shopify/ui-extensions-react/admin';

export default reactExtension('admin.product-details.action.render', () => <Extension />);

function Extension() {
  const { data } = useData();

  async function handleExport() {
    const response = await fetch('/api/export', {
      method: 'POST',
      body: JSON.stringify({ productId: data.product.id })
    });
    console.log('Exported:', await response.json());
  }

  return (
    <AdminAction
      title="Export Product"
      primaryAction={<Button onPress={handleExport}>Export</Button>}
    />
  );
}
```

**Targets:**
- `admin.product-details.action.render`
- `admin.order-details.action.render`
- `admin.customer-details.action.render`

### Admin Block

Embedded content in admin pages.

```javascript
import { reactExtension, BlockStack, Text, Badge } from '@shopify/ui-extensions-react/admin';

export default reactExtension('admin.product-details.block.render', () => <Extension />);

function Extension() {
  const { data } = useData();
  const [analytics, setAnalytics] = useState(null);

  useEffect(() => {
    fetchAnalytics(data.product.id).then(setAnalytics);
  }, []);

  return (
    <BlockStack>
      <Text variant="headingMd">Product Analytics</Text>
      <Text>Views: {analytics?.views || 0}</Text>
      <Text>Conversions: {analytics?.conversions || 0}</Text>
      <Badge tone={analytics?.trending ? "success" : "info"}>
        {analytics?.trending ? "Trending" : "Normal"}
      </Badge>
    </BlockStack>
  );
}
```

**Targets:**
- `admin.product-details.block.render`
- `admin.order-details.block.render`
- `admin.customer-details.block.render`

## POS UI Extensions

Customize Point of Sale experience.

### Smart Grid Tile

Quick access action on POS home screen.

```javascript
import { reactExtension, SmartGridTile } from '@shopify/ui-extensions-react/pos';

export default reactExtension('pos.home.tile.render', () => <Extension />);

function Extension() {
  function handlePress() {
    // Navigate to custom workflow
  }

  return (
    <SmartGridTile
      title="Gift Cards"
      subtitle="Manage gift cards"
      onPress={handlePress}
    />
  );
}
```

### POS Modal

Full-screen workflow.

```javascript
import { reactExtension, Screen, BlockStack, Button, TextField } from '@shopify/ui-extensions-react/pos';

export default reactExtension('pos.home.modal.render', () => <Extension />);

function Extension() {
  const { navigation } = useApi();
  const [amount, setAmount] = useState('');

  function handleIssue() {
    // Issue gift card
    navigation.pop();
  }

  return (
    <Screen name="Gift Card" title="Issue Gift Card">
      <BlockStack>
        <TextField label="Amount" value={amount} onChange={setAmount} />
        <TextField label="Recipient Email" />
        <Button onPress={handleIssue}>Issue</Button>
      </BlockStack>
    </Screen>
  );
}
```

## Customer Account Extensions

Customize customer account pages.

### Order Status Extension

```javascript
import { reactExtension, BlockStack, Text, Button } from '@shopify/ui-extensions-react/customer-account';

export default reactExtension('customer-account.order-status.block.render', () => <Extension />);

function Extension() {
  const { order } = useApi();

  function handleReturn() {
    // Initiate return
  }

  return (
    <BlockStack>
      <Text variant="headingMd">Need to return?</Text>
      <Text>Start return for order {order.name}</Text>
      <Button onPress={handleReturn}>Start Return</Button>
    </BlockStack>
  );
}
```

**Targets:**
- `customer-account.order-status.block.render`
- `customer-account.order-index.block.render`
- `customer-account.profile.block.render`

## Shopify Functions

Serverless backend customization.

### Function Types

**Discounts:**
- `order_discount` - Order-level discounts
- `product_discount` - Product-specific discounts
- `shipping_discount` - Shipping discounts

**Payment Customization:**
- Hide/rename/reorder payment methods

**Delivery Customization:**
- Custom shipping options
- Delivery rules

**Validation:**
- Cart validation rules
- Checkout validation

### Create Function

```bash
shopify app generate extension --type function
```

### Order Discount Function

```javascript
// input.graphql
query Input {
  cart {
    lines {
      quantity
      merchandise {
        ... on ProductVariant {
          product {
            hasTag(tag: "bulk-discount")
          }
        }
      }
    }
  }
}

// function.js
export default function orderDiscount(input) {
  const targets = input.cart.lines
    .filter(line => line.merchandise.product.hasTag)
    .map(line => ({
      productVariant: { id: line.merchandise.id }
    }));

  if (targets.length === 0) {
    return { discounts: [] };
  }

  return {
    discounts: [{
      targets,
      value: {
        percentage: {
          value: 10  // 10% discount
        }
      }
    }]
  };
}
```

### Payment Customization Function

```javascript
export default function paymentCustomization(input) {
  const hidePaymentMethods = input.cart.lines.some(
    line => line.merchandise.product.hasTag
  );

  if (!hidePaymentMethods) {
    return { operations: [] };
  }

  return {
    operations: [{
      hide: {
        paymentMethodId: "gid://shopify/PaymentMethod/123"
      }
    }]
  };
}
```

### Validation Function

```javascript
export default function cartValidation(input) {
  const errors = [];

  // Max 5 items per cart
  if (input.cart.lines.length > 5) {
    errors.push({
      localizedMessage: "Maximum 5 items allowed per order",
      target: "cart"
    });
  }

  // Min $50 for wholesale
  const isWholesale = input.cart.lines.some(
    line => line.merchandise.product.hasTag
  );

  if (isWholesale && input.cart.cost.totalAmount.amount < 50) {
    errors.push({
      localizedMessage: "Wholesale orders require $50 minimum",
      target: "cart"
    });
  }

  return { errors };
}
```

## Network Requests

Extensions can call external APIs.

```javascript
import { useApi } from '@shopify/ui-extensions-react/checkout';

function Extension() {
  const { sessionToken } = useApi();

  async function fetchData() {
    const token = await sessionToken.get();

    const response = await fetch('https://your-app.com/api/data', {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });

    return await response.json();
  }
}
```

## Best Practices

**Performance:**
- Lazy load data
- Memoize expensive computations
- Use loading states
- Minimize re-renders

**UX:**
- Provide clear error messages
- Show loading indicators
- Validate inputs
- Support keyboard navigation

**Security:**
- Verify session tokens on backend
- Sanitize user input
- Use HTTPS for all requests
- Don't expose sensitive data

**Testing:**
- Test on development stores
- Verify mobile/desktop
- Check accessibility
- Test edge cases

## Resources

- Checkout Extensions: https://shopify.dev/docs/api/checkout-extensions
- Admin Extensions: https://shopify.dev/docs/apps/admin/extensions
- Functions: https://shopify.dev/docs/apps/functions
- Components: https://shopify.dev/docs/api/checkout-ui-extensions/components

```

### references/themes.md

```markdown
# Themes Reference

Guide for developing Shopify themes with Liquid templating.

## Liquid Templating

### Syntax Basics

**Objects (Output):**
```liquid
{{ product.title }}
{{ product.price | money }}
{{ customer.email }}
```

**Tags (Logic):**
```liquid
{% if product.available %}
  <button>Add to Cart</button>
{% else %}
  <p>Sold Out</p>
{% endif %}

{% for product in collection.products %}
  {{ product.title }}
{% endfor %}

{% case product.type %}
  {% when 'Clothing' %}
    <span>Apparel</span>
  {% when 'Shoes' %}
    <span>Footwear</span>
  {% else %}
    <span>Other</span>
{% endcase %}
```

**Filters (Transform):**
```liquid
{{ product.title | upcase }}
{{ product.price | money }}
{{ product.description | strip_html | truncate: 100 }}
{{ product.image | img_url: 'medium' }}
{{ 'now' | date: '%B %d, %Y' }}
```

### Common Objects

**Product:**
```liquid
{{ product.id }}
{{ product.title }}
{{ product.handle }}
{{ product.description }}
{{ product.price }}
{{ product.compare_at_price }}
{{ product.available }}
{{ product.type }}
{{ product.vendor }}
{{ product.tags }}
{{ product.images }}
{{ product.variants }}
{{ product.featured_image }}
{{ product.url }}
```

**Collection:**
```liquid
{{ collection.title }}
{{ collection.handle }}
{{ collection.description }}
{{ collection.products }}
{{ collection.products_count }}
{{ collection.image }}
{{ collection.url }}
```

**Cart:**
```liquid
{{ cart.item_count }}
{{ cart.total_price }}
{{ cart.items }}
{{ cart.note }}
{{ cart.attributes }}
```

**Customer:**
```liquid
{{ customer.email }}
{{ customer.first_name }}
{{ customer.last_name }}
{{ customer.orders_count }}
{{ customer.total_spent }}
{{ customer.addresses }}
{{ customer.default_address }}
```

**Shop:**
```liquid
{{ shop.name }}
{{ shop.email }}
{{ shop.domain }}
{{ shop.currency }}
{{ shop.money_format }}
{{ shop.enabled_payment_types }}
```

### Common Filters

**String:**
- `upcase`, `downcase`, `capitalize`
- `strip_html`, `strip_newlines`
- `truncate: 100`, `truncatewords: 20`
- `replace: 'old', 'new'`

**Number:**
- `money` - Format currency
- `round`, `ceil`, `floor`
- `times`, `divided_by`, `plus`, `minus`

**Array:**
- `join: ', '`
- `first`, `last`
- `size`
- `map: 'property'`
- `where: 'property', 'value'`

**URL:**
- `img_url: 'size'` - Image URL
- `url_for_type`, `url_for_vendor`
- `link_to`, `link_to_type`

**Date:**
- `date: '%B %d, %Y'`

## Theme Architecture

### Directory Structure

```
theme/
├── assets/              # CSS, JS, images
├── config/              # Theme settings
│   ├── settings_schema.json
│   └── settings_data.json
├── layout/              # Base templates
│   └── theme.liquid
├── locales/             # Translations
│   └── en.default.json
├── sections/            # Reusable blocks
│   ├── header.liquid
│   ├── footer.liquid
│   └── product-grid.liquid
├── snippets/            # Small components
│   ├── product-card.liquid
│   └── icon.liquid
└── templates/           # Page templates
    ├── index.json
    ├── product.json
    ├── collection.json
    └── cart.liquid
```

### Layout

Base template wrapping all pages (`layout/theme.liquid`):

```liquid
<!DOCTYPE html>
<html lang="{{ request.locale.iso_code }}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>{{ page_title }}</title>

  {{ content_for_header }}

  <link rel="stylesheet" href="{{ 'theme.css' | asset_url }}">
</head>
<body>
  {% section 'header' %}

  <main>
    {{ content_for_layout }}
  </main>

  {% section 'footer' %}

  <script src="{{ 'theme.js' | asset_url }}"></script>
</body>
</html>
```

### Templates

Page-specific structures (`templates/product.json`):

```json
{
  "sections": {
    "main": {
      "type": "product-template",
      "settings": {
        "show_vendor": true,
        "show_quantity_selector": true
      }
    },
    "recommendations": {
      "type": "product-recommendations"
    }
  },
  "order": ["main", "recommendations"]
}
```

Legacy format (`templates/product.liquid`):
```liquid
<div class="product">
  <div class="product-images">
    <img src="{{ product.featured_image | img_url: 'large' }}" alt="{{ product.title }}">
  </div>

  <div class="product-details">
    <h1>{{ product.title }}</h1>
    <p class="price">{{ product.price | money }}</p>

    {% form 'product', product %}
      <select name="id">
        {% for variant in product.variants %}
          <option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
        {% endfor %}
      </select>

      <button type="submit">Add to Cart</button>
    {% endform %}
  </div>
</div>
```

### Sections

Reusable content blocks (`sections/product-grid.liquid`):

```liquid
<div class="product-grid">
  {% for product in section.settings.collection.products %}
    <div class="product-card">
      <a href="{{ product.url }}">
        <img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
        <h3>{{ product.title }}</h3>
        <p>{{ product.price | money }}</p>
      </a>
    </div>
  {% endfor %}
</div>

{% schema %}
{
  "name": "Product Grid",
  "settings": [
    {
      "type": "collection",
      "id": "collection",
      "label": "Collection"
    },
    {
      "type": "range",
      "id": "products_per_row",
      "min": 2,
      "max": 5,
      "step": 1,
      "default": 4,
      "label": "Products per row"
    }
  ],
  "presets": [
    {
      "name": "Product Grid"
    }
  ]
}
{% endschema %}
```

### Snippets

Small reusable components (`snippets/product-card.liquid`):

```liquid
<div class="product-card">
  <a href="{{ product.url }}">
    {% if product.featured_image %}
      <img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
    {% endif %}
    <h3>{{ product.title }}</h3>
    <p class="price">{{ product.price | money }}</p>
    {% if product.compare_at_price > product.price %}
      <p class="sale-price">{{ product.compare_at_price | money }}</p>
    {% endif %}
  </a>
</div>
```

Include snippet:
```liquid
{% render 'product-card', product: product %}
```

## Development Workflow

### Setup

```bash
# Initialize new theme
shopify theme init

# Choose Dawn (reference theme) or blank
```

### Local Development

```bash
# Start local server
shopify theme dev

# Preview at http://localhost:9292
# Changes auto-sync to development theme
```

### Pull Theme

```bash
# Pull live theme
shopify theme pull --live

# Pull specific theme
shopify theme pull --theme=123456789

# Pull only templates
shopify theme pull --only=templates
```

### Push Theme

```bash
# Push to development theme
shopify theme push --development

# Create new unpublished theme
shopify theme push --unpublished

# Push specific files
shopify theme push --only=sections,snippets
```

### Theme Check

Lint theme code:
```bash
shopify theme check
shopify theme check --auto-correct
```

## Common Patterns

### Product Form with Variants

```liquid
{% form 'product', product %}
  {% unless product.has_only_default_variant %}
    {% for option in product.options_with_values %}
      <div class="product-option">
        <label>{{ option.name }}</label>
        <select name="options[{{ option.name }}]">
          {% for value in option.values %}
            <option value="{{ value }}">{{ value }}</option>
          {% endfor %}
        </select>
      </div>
    {% endfor %}
  {% endunless %}

  <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
  <input type="number" name="quantity" value="1" min="1">

  <button type="submit" {% unless product.available %}disabled{% endunless %}>
    {% if product.available %}Add to Cart{% else %}Sold Out{% endif %}
  </button>
{% endform %}
```

### Pagination

```liquid
{% paginate collection.products by 12 %}
  {% for product in collection.products %}
    {% render 'product-card', product: product %}
  {% endfor %}

  {% if paginate.pages > 1 %}
    <div class="pagination">
      {% if paginate.previous %}
        <a href="{{ paginate.previous.url }}">Previous</a>
      {% endif %}

      {% for part in paginate.parts %}
        {% if part.is_link %}
          <a href="{{ part.url }}">{{ part.title }}</a>
        {% else %}
          <span class="current">{{ part.title }}</span>
        {% endif %}
      {% endfor %}

      {% if paginate.next %}
        <a href="{{ paginate.next.url }}">Next</a>
      {% endif %}
    </div>
  {% endif %}
{% endpaginate %}
```

### Cart AJAX

```javascript
// Add to cart
fetch('/cart/add.js', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    id: variantId,
    quantity: 1
  })
})
.then(res => res.json())
.then(item => console.log('Added:', item));

// Get cart
fetch('/cart.js')
  .then(res => res.json())
  .then(cart => console.log('Cart:', cart));

// Update cart
fetch('/cart/change.js', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    id: lineItemKey,
    quantity: 2
  })
})
.then(res => res.json());
```

## Metafields in Themes

Access custom data:

```liquid
{{ product.metafields.custom.care_instructions }}
{{ product.metafields.custom.material.value }}

{% if product.metafields.custom.featured %}
  <span class="badge">Featured</span>
{% endif %}
```

## Best Practices

**Performance:**
- Optimize images (use appropriate sizes)
- Minimize Liquid logic complexity
- Use lazy loading for images
- Defer non-critical JavaScript

**Accessibility:**
- Use semantic HTML
- Include alt text for images
- Support keyboard navigation
- Ensure sufficient color contrast

**SEO:**
- Use descriptive page titles
- Include meta descriptions
- Structure content with headings
- Implement schema markup

**Code Quality:**
- Follow Shopify theme guidelines
- Use consistent naming conventions
- Comment complex logic
- Keep sections focused and reusable

## Resources

- Theme Development: https://shopify.dev/docs/themes
- Liquid Reference: https://shopify.dev/docs/api/liquid
- Dawn Theme: https://github.com/Shopify/dawn
- Theme Check: https://shopify.dev/docs/themes/tools/theme-check

```

### scripts/shopify_init.py

```python
#!/usr/bin/env python3
"""
Shopify Project Initialization Script

Interactive script to scaffold Shopify apps, extensions, or themes.
Supports environment variable loading from multiple locations.
"""

import os
import sys
import json
import subprocess
from pathlib import Path
from typing import Dict, Optional, List
from dataclasses import dataclass


@dataclass
class EnvConfig:
    """Environment configuration container."""
    shopify_api_key: Optional[str] = None
    shopify_api_secret: Optional[str] = None
    shop_domain: Optional[str] = None
    scopes: Optional[str] = None


class EnvLoader:
    """Load environment variables from multiple sources in priority order."""

    @staticmethod
    def load_env_file(filepath: Path) -> Dict[str, str]:
        """
        Load environment variables from .env file.

        Args:
            filepath: Path to .env file

        Returns:
            Dictionary of environment variables
        """
        env_vars = {}
        if not filepath.exists():
            return env_vars

        try:
            with open(filepath, 'r') as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#') and '=' in line:
                        key, value = line.split('=', 1)
                        env_vars[key.strip()] = value.strip().strip('"').strip("'")
        except Exception as e:
            print(f"Warning: Failed to load {filepath}: {e}")

        return env_vars

    @staticmethod
    def get_env_paths(skill_dir: Path) -> List[Path]:
        """
        Get list of .env file paths in priority order.

        Priority: process.env > skill/.env > skills/.env > .claude/.env

        Args:
            skill_dir: Path to skill directory

        Returns:
            List of .env file paths
        """
        paths = []

        # skill/.env
        skill_env = skill_dir / '.env'
        if skill_env.exists():
            paths.append(skill_env)

        # skills/.env
        skills_env = skill_dir.parent / '.env'
        if skills_env.exists():
            paths.append(skills_env)

        # .claude/.env
        claude_env = skill_dir.parent.parent / '.env'
        if claude_env.exists():
            paths.append(claude_env)

        return paths

    @staticmethod
    def load_config(skill_dir: Path) -> EnvConfig:
        """
        Load configuration from environment variables.

        Priority: process.env > skill/.env > skills/.env > .claude/.env

        Args:
            skill_dir: Path to skill directory

        Returns:
            EnvConfig object
        """
        config = EnvConfig()

        # Load from .env files (reverse priority order)
        for env_path in reversed(EnvLoader.get_env_paths(skill_dir)):
            env_vars = EnvLoader.load_env_file(env_path)
            if 'SHOPIFY_API_KEY' in env_vars:
                config.shopify_api_key = env_vars['SHOPIFY_API_KEY']
            if 'SHOPIFY_API_SECRET' in env_vars:
                config.shopify_api_secret = env_vars['SHOPIFY_API_SECRET']
            if 'SHOP_DOMAIN' in env_vars:
                config.shop_domain = env_vars['SHOP_DOMAIN']
            if 'SCOPES' in env_vars:
                config.scopes = env_vars['SCOPES']

        # Override with process environment (highest priority)
        if 'SHOPIFY_API_KEY' in os.environ:
            config.shopify_api_key = os.environ['SHOPIFY_API_KEY']
        if 'SHOPIFY_API_SECRET' in os.environ:
            config.shopify_api_secret = os.environ['SHOPIFY_API_SECRET']
        if 'SHOP_DOMAIN' in os.environ:
            config.shop_domain = os.environ['SHOP_DOMAIN']
        if 'SCOPES' in os.environ:
            config.scopes = os.environ['SCOPES']

        return config


class ShopifyInitializer:
    """Initialize Shopify projects."""

    def __init__(self, config: EnvConfig):
        """
        Initialize ShopifyInitializer.

        Args:
            config: Environment configuration
        """
        self.config = config

    def prompt(self, message: str, default: Optional[str] = None) -> str:
        """
        Prompt user for input.

        Args:
            message: Prompt message
            default: Default value

        Returns:
            User input or default
        """
        if default:
            message = f"{message} [{default}]"
        user_input = input(f"{message}: ").strip()
        return user_input if user_input else (default or '')

    def select_option(self, message: str, options: List[str]) -> str:
        """
        Prompt user to select from options.

        Args:
            message: Prompt message
            options: List of options

        Returns:
            Selected option
        """
        print(f"\n{message}")
        for i, option in enumerate(options, 1):
            print(f"{i}. {option}")

        while True:
            try:
                choice = int(input("Select option: ").strip())
                if 1 <= choice <= len(options):
                    return options[choice - 1]
                print(f"Please select 1-{len(options)}")
            except (ValueError, KeyboardInterrupt):
                print("Invalid input")

    def check_cli_installed(self) -> bool:
        """
        Check if Shopify CLI is installed.

        Returns:
            True if installed, False otherwise
        """
        try:
            result = subprocess.run(
                ['shopify', 'version'],
                capture_output=True,
                text=True,
                timeout=5
            )
            return result.returncode == 0
        except (subprocess.SubprocessError, FileNotFoundError):
            return False

    def create_app_config(self, project_dir: Path, app_name: str, scopes: str) -> None:
        """
        Create shopify.app.toml configuration file.

        Args:
            project_dir: Project directory
            app_name: Application name
            scopes: Access scopes
        """
        config_content = f"""# Shopify App Configuration
name = "{app_name}"
client_id = "{self.config.shopify_api_key or 'YOUR_API_KEY'}"
application_url = "https://your-app.com"
embedded = true

[build]
automatically_update_urls_on_dev = true
dev_store_url = "{self.config.shop_domain or 'your-store.myshopify.com'}"

[access_scopes]
scopes = "{scopes}"

[webhooks]
api_version = "2025-01"

[[webhooks.subscriptions]]
topics = ["app/uninstalled"]
uri = "/webhooks/app/uninstalled"

[webhooks.privacy_compliance]
customer_data_request_url = "/webhooks/gdpr/data-request"
customer_deletion_url = "/webhooks/gdpr/customer-deletion"
shop_deletion_url = "/webhooks/gdpr/shop-deletion"
"""
        config_path = project_dir / 'shopify.app.toml'
        config_path.write_text(config_content)
        print(f"✓ Created {config_path}")

    def create_extension_config(self, project_dir: Path, extension_name: str, extension_type: str) -> None:
        """
        Create shopify.extension.toml configuration file.

        Args:
            project_dir: Project directory
            extension_name: Extension name
            extension_type: Extension type
        """
        target_map = {
            'checkout': 'purchase.checkout.block.render',
            'admin_action': 'admin.product-details.action.render',
            'admin_block': 'admin.product-details.block.render',
            'pos': 'pos.home.tile.render'
        }

        config_content = f"""name = "{extension_name}"
type = "ui_extension"
handle = "{extension_name.lower().replace(' ', '-')}"

[extension_points]
api_version = "2025-01"

[[extension_points.targets]]
target = "{target_map.get(extension_type, 'purchase.checkout.block.render')}"

[capabilities]
network_access = true
api_access = true
"""
        config_path = project_dir / 'shopify.extension.toml'
        config_path.write_text(config_content)
        print(f"✓ Created {config_path}")

    def create_readme(self, project_dir: Path, project_type: str, project_name: str) -> None:
        """
        Create README.md file.

        Args:
            project_dir: Project directory
            project_type: Project type (app/extension/theme)
            project_name: Project name
        """
        content = f"""# {project_name}

Shopify {project_type.capitalize()} project.

## Setup

```bash
# Install dependencies
npm install

# Start development
shopify {project_type} dev
```

## Deployment

```bash
# Deploy to Shopify
shopify {project_type} deploy
```

## Resources

- [Shopify Documentation](https://shopify.dev/docs)
- [Shopify CLI](https://shopify.dev/docs/api/shopify-cli)
"""
        readme_path = project_dir / 'README.md'
        readme_path.write_text(content)
        print(f"✓ Created {readme_path}")

    def init_app(self) -> None:
        """Initialize Shopify app project."""
        print("\n=== Shopify App Initialization ===\n")

        app_name = self.prompt("App name", "my-shopify-app")
        scopes = self.prompt("Access scopes", self.config.scopes or "read_products,write_products")

        project_dir = Path.cwd() / app_name
        project_dir.mkdir(exist_ok=True)

        print(f"\nCreating app in {project_dir}...")

        self.create_app_config(project_dir, app_name, scopes)
        self.create_readme(project_dir, "app", app_name)

        # Create basic package.json
        package_json = {
            "name": app_name.lower().replace(' ', '-'),
            "version": "1.0.0",
            "scripts": {
                "dev": "shopify app dev",
                "deploy": "shopify app deploy"
            }
        }
        (project_dir / 'package.json').write_text(json.dumps(package_json, indent=2))
        print(f"✓ Created package.json")

        print(f"\n✓ App '{app_name}' initialized successfully!")
        print(f"\nNext steps:")
        print(f"  cd {app_name}")
        print(f"  npm install")
        print(f"  shopify app dev")

    def init_extension(self) -> None:
        """Initialize Shopify extension project."""
        print("\n=== Shopify Extension Initialization ===\n")

        extension_types = ['checkout', 'admin_action', 'admin_block', 'pos']
        extension_type = self.select_option("Select extension type", extension_types)

        extension_name = self.prompt("Extension name", "my-extension")

        project_dir = Path.cwd() / extension_name
        project_dir.mkdir(exist_ok=True)

        print(f"\nCreating extension in {project_dir}...")

        self.create_extension_config(project_dir, extension_name, extension_type)
        self.create_readme(project_dir, "extension", extension_name)

        print(f"\n✓ Extension '{extension_name}' initialized successfully!")
        print(f"\nNext steps:")
        print(f"  cd {extension_name}")
        print(f"  shopify app dev")

    def init_theme(self) -> None:
        """Initialize Shopify theme project."""
        print("\n=== Shopify Theme Initialization ===\n")

        theme_name = self.prompt("Theme name", "my-theme")

        print(f"\nInitializing theme '{theme_name}'...")
        print("\nRecommended: Use 'shopify theme init' for full theme scaffolding")
        print(f"\nRun: shopify theme init {theme_name}")

    def run(self) -> None:
        """Run interactive initialization."""
        print("=" * 60)
        print("Shopify Project Initializer")
        print("=" * 60)

        # Check CLI
        if not self.check_cli_installed():
            print("\n⚠ Shopify CLI not found!")
            print("Install: npm install -g @shopify/cli@latest")
            sys.exit(1)

        # Select project type
        project_types = ['app', 'extension', 'theme']
        project_type = self.select_option("Select project type", project_types)

        # Initialize based on type
        if project_type == 'app':
            self.init_app()
        elif project_type == 'extension':
            self.init_extension()
        elif project_type == 'theme':
            self.init_theme()


def main() -> None:
    """Main entry point."""
    try:
        # Get skill directory
        script_dir = Path(__file__).parent
        skill_dir = script_dir.parent

        # Load configuration
        config = EnvLoader.load_config(skill_dir)

        # Initialize project
        initializer = ShopifyInitializer(config)
        initializer.run()

    except KeyboardInterrupt:
        print("\n\nAborted.")
        sys.exit(0)
    except Exception as e:
        print(f"\n✗ Error: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == '__main__':
    main()

```

shopify | SkillHub