Skip to main content
rule

snowflake-snowpark-dbt-cursorrules-prompt-file — Cursor rule

A Cursor rule that gives the agent context for Snowpark Python and dbt with the Snowflake adapter. Useful when you write transformation pipelines in either tool.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/snowflake-snowpark-dbt-cursorrules-prompt-file.mdc
Open on GitHub

What snowflake-snowpark-dbt-cursorrules-prompt-file does

The rule spells out how the agent should handle Snowpark Python, Snowflake's server-side Python API. It covers Session, DataFrame, UDF, UDTF, UDAF and stored procedures, lazy evaluation, and operations such as filter, select, group_by().agg(), join and save_as_table(). A second block covers dbt with the dbt-snowflake adapter: materializations, incremental models, dynamic tables, and configs like cluster_by, transient, query_tag, copy_grants and snowflake_warehouse.

The rule ships with alwaysApply: false, so it does not attach itself to every file — you wire it up deliberately. Installation is a single file, .cursor/rules/snowflake-snowpark-dbt-cursorrules-prompt-file.mdc, dropped into the project. For the dbt side you need pip install dbt-snowflake and a filled-in profiles.yml; the Session example reads credentials from SNOWFLAKE_ACCOUNT, SNOWFLAKE_USER and SNOWFLAKE_PASSWORD.

The anti-pattern list is the part worth reading first. The agent won't pull large DataFrames to the client with collect(), won't loop over rows in Python instead of using vectorized operations, and won't set cluster_by on small tables. It also won't use {{ this }} without an {% if is_incremental() %} guard.

What it helps with

  • build a Snowpark Session from environment variables
  • write an incremental dbt model with a unique key and merge strategy
  • register a vectorized UDF for model inference
  • declare sources and tests in schema.yml
  • pick a materialization for a fact table

Example prompts for the agent

  • Write a Snowpark DataFrame that filters customers by region and sums revenue
  • Create an incremental dbt model for stg_events with on_schema_change sync_all_columns
  • Check my dbt project against the anti-patterns in the rule

How to install snowflake-snowpark-dbt-cursorrules-prompt-file

Manually: put the files in place

.cursor/rules/snowflake-snowpark-dbt-cursorrules-prompt-file.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about snowflake-snowpark-dbt-cursorrules-prompt-file

How do I install snowflake-snowpark-dbt-cursorrules-prompt-file in Cursor?

Copy .cursor/rules/snowflake-snowpark-dbt-cursorrules-prompt-file.mdc into your project — that's the whole step. The rule is marked alwaysApply: false, so attach it to the files you want.

What does this rule do?

It hands the Cursor agent context for Snowpark Python and dbt with the Snowflake adapter: the DataFrame API, UDFs and UDTFs, stored procedures, materializations and model configs. It also lists anti-patterns the agent should avoid.

What do I need for the dbt side?

The dbt-snowflake package and a filled-in profiles.yml with account, role, database and warehouse. The Session example reads credentials from SNOWFLAKE_ACCOUNT, SNOWFLAKE_USER and SNOWFLAKE_PASSWORD.

Source text

snowflake-snowpark-dbt-cursorrules-prompt-file.mdc · CC0-1.0
---
description: "Cursor rules for Snowpark Python (DataFrames, UDFs, stored procedures) and dbt with the Snowflake adapter."
globs: **/*
alwaysApply: false
---
// Snowflake Snowpark Python & dbt
// Expert guidance for Snowpark Python development and dbt with the Snowflake adapter

You are an expert in Snowpark Python (Snowflake's server-side Python API) and dbt with the dbt-snowflake adapter. You build production-grade data transformation pipelines using both tools.

// ═══════════════════════════════════════════
// SNOWPARK PYTHON
// ═══════════════════════════════════════════

// Snowpark runs Python server-side in Snowflake warehouses. Data never leaves Snowflake.
// Core abstractions: Session, DataFrame, UDF, UDTF, UDAF, Stored Procedure.

// Session
from snowflake.snowpark import Session
import os
session = Session.builder.configs({
    "account": os.environ["SNOWFLAKE_ACCOUNT"],
    "user": os.environ["SNOWFLAKE_USER"],
    "password": os.environ["SNOWFLAKE_PASSWORD"],
    "role": "my_role", "warehouse": "my_wh", "database": "my_db", "schema": "my_schema"
}).create()

// DataFrame API — Lazy evaluation, builds query plan executed on collect()/show().
df = session.table("customers")
df_filtered = df.filter(df["region"] == "US").select("name", "email", "revenue")
df_agg = df.group_by("region").agg(sum("revenue").alias("total_revenue"))
df_agg.show()

// Key operations: .filter(), .select(), .group_by().agg(), .join(), .sort(),
// .with_column(), .drop(), .distinct(), .limit(), .union_all(), .flatten(),
// .write.save_as_table()

// Scalar UDFs
from snowflake.snowpark.functions import udf
@udf(name="normalize_email", replace=True)
def normalize_email(email: str) -> str:
    return email.strip().lower() if email else None

// Vectorized UDFs (10-100x faster for ML inference):
import pandas as pd
@udf(name="predict_score", packages=["scikit-learn", "pandas"], replace=True)
def predict_score(features: pd.Series) -> pd.Series:
    import pickle, sys
    model = pickle.load(open(sys.path[0] + "/model.pkl", "rb"))
    return pd.Series(model.predict(features.values.reshape(-1, 1)))

// UDTFs (return multiple rows per input):
class Tokenizer:
    def process(self, text: str):
        for token in text.split():
            yield (token,)

tokenize = session.udtf.register(Tokenizer,
    output_schema=StructType([StructField("token", StringType())]),
    input_types=[StringType()], name="tokenize", replace=True)

// Stored Procedures (server-side multi-step logic):
from snowflake.snowpark.functions import sproc
@sproc(name="daily_etl", replace=True, packages=["snowflake-snowpark-python"])
def daily_etl(session: Session) -> str:
    raw = session.table("raw_events")
    cleaned = raw.filter(raw["event_type"].is_not_null())
    cleaned.write.mode("overwrite").save_as_table("cleaned_events")
    return f"Processed {cleaned.count()} rows"

// Third-Party Packages: session.add_packages("pandas", "scikit-learn==1.3.0", "xgboost")
// File Access: session.add_import("@my_stage/model.pkl") for static files.
// pandas on Snowflake (no data movement):
//   import modin.pandas as pd; import snowflake.snowpark.modin.plugin
//   df = pd.read_snowflake("my_table")

// ═══════════════════════════════════════════
// DBT WITH SNOWFLAKE ADAPTER
// ═══════════════════════════════════════════

// Install: pip install dbt-snowflake
// profiles.yml:
my_project:
  target: dev
  outputs:
    dev:
      type: snowflake
      account: myaccount
      user: myuser
      password: "{{ env_var('SNOWFLAKE_PASSWORD') }}"
      role: transformer
      database: analytics
      warehouse: transforming
      schema: public
      threads: 4

// Materializations: view, table, incremental, ephemeral, dynamic_table

// Dynamic Tables in dbt:
// {{ config(materialized='dynamic_table', snowflake_warehouse='transforming', target_lag='1 hour') }}
// SELECT customer_id, SUM(amount) AS lifetime_value FROM {{ ref('stg_orders') }} GROUP BY 1

// Incremental Models:
{{
  config(
    materialized='incremental',
    unique_key='event_id',
    incremental_strategy='merge',
    on_schema_change='sync_all_columns'
  )
}}
SELECT * FROM {{ ref('stg_events') }}
{% if is_incremental() %}
  WHERE event_timestamp > (SELECT MAX(event_timestamp) FROM {{ this }})
{% endif %}

// Snowflake-Specific Configs:
// cluster_by=['col1', 'col2']  — Clustering (large tables only)
// transient=true               — No Fail-safe (lower storage cost)
// query_tag='finance_daily'    — Workload attribution
// copy_grants=true             — Preserve access on replace
// snowflake_warehouse='lg_wh'  — Per-model warehouse override
// secure=true                  — Secure views

// Sources (models/staging/_sources.yml):
sources:
  - name: raw
    database: raw_db
    schema: jaffle_shop
    tables:
      - name: customers
        loaded_at_field: _loaded_at
        freshness:
          warn_after: {count: 12, period: hour}
          error_after: {count: 24, period: hour}

// Testing (schema.yml):
models:
  - name: stg_customers
    columns:
      - name: customer_id
        tests: [unique, not_null]

// Key Commands:
// dbt run, dbt test, dbt build (run+test in order), dbt compile
// dbt run --select my_model+  (model + downstream)
// dbt run --select +my_model  (model + upstream)
// dbt source freshness, dbt docs generate && dbt docs serve

// Custom schema macro (macros/generate_schema_name.sql):
{% macro generate_schema_name(custom_schema_name, node) %}
  {% if custom_schema_name %}{{ custom_schema_name | trim }}{% else %}{{ target.schema }}{% endif %}
{% endmacro %}

// Best Practices
- Prefer vectorized UDFs (pandas) for ML inference — much faster than scalar UDFs.
- Pin package versions in production UDFs and stored procedures.
- Use DataFrame API over raw SQL strings in reusable Python pipelines.
- Use staging models (stg_*) to rename/type-cast, mart models for business tables.
- Use incremental for fact tables; dynamic_table for near-real-time.
- Set on_schema_change='sync_all_columns' on incremental models.
- Use copy_grants=true to avoid permission issues. Tag models for selective execution.
- Use separate warehouses for dbt runs vs analyst queries.

// Anti-Patterns
- Do NOT collect() large DataFrames to client — process server-side.
- Do NOT use Python loops over rows — use DataFrame operations or vectorized UDFs.
- Do NOT use {{ this }} without {% if is_incremental() %} guard.
- Do NOT set cluster_by on small tables (< 1TB).
- Do NOT use materialized='table' for everything — views are free.
- Do NOT hardcode database/schema — use {{ ref() }} and {{ source() }}.

© PatrickJS · CC0-1.0 · PatrickJS/awesome-cursorrules

Similar IDE rules

All IDE rules for Data and databases →

From the same set68

sveltekit-typescript-guide-cursorrules-prompt-file A Cursor rule that points the agent at Svelte 5, SvelteKit and TypeScript: project structure, runes, state handling. ★ 41,011repo stars svelte A Cursor rule that steers the agent toward Svelte conventions: component structure, reactivity, stores, SvelteKit routing and tests. Useful when the agent writes or edits Svelte code. ★ 41,011repo stars solidjs-tailwind-cursorrules-prompt-file A Cursor rule that tells the agent how to write Solid.js code with Tailwind CSS: signals, utility classes, folder layout. ★ 41,011repo stars solidity-hardhat-cursorrules-prompt-file A Cursor rule that turns the AI assistant into a Solidity and smart contract security expert, with Hardhat-based testing and gas work. ★ 41,011repo stars shopify-theme-dev-liquid A Cursor rule collecting Shopify theme practices across Liquid, JavaScript, CSS, accessibility and performance. Useful when you build a theme and want the agent to keep the structure. ★ 41,011repo stars salesforce-apex-cursorrules-prompt-file A Cursor rule that points the agent at Salesforce Apex work: queueables instead of @future, GoF patterns, and almost no comments. ★ 41,011repo stars rtl-right-to-left-i18n-cursorrules-prompt-file A Cursor rule that pushes the agent toward logical CSS and Tailwind utilities instead of left/right. Useful when the app has to run in Arabic, Hebrew, Persian or Urdu. ★ 41,011repo stars react This rule hands Cursor a set of React conventions: component structure, hooks, state, forms and accessibility. Useful when a project has plenty of .tsx and .jsx and you want one style. ★ 41,011repo stars

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor