CLI Reference
Installation
Binary is distributed as lexega-sql (or lexega-sql.exe on Windows).
Commands
Init (Project Setup)
# Initialize in current directory
lexega-sql init
# Preview without writing files
lexega-sql init --dry-run
# Include CI workflow (GitHub Actions or GitLab CI)
lexega-sql init --with-ci
# Initialize specific directory
lexega-sql init /path/to/project
Creates:
.lexega/policy.yml— Permissive baseline policy (warns on critical/high, nothing blocked).lexega/exceptions.yml— Scaffold for approved overrides.lexega/reports/baseline.sarif— SARIF evidence snapshot (visible in the dashboard).lexega/decisions/— Directory for decision artifacts
See Policy Reference for details on policy configuration.
Semantic Diff (Primary Command)
# Diff between git refs
lexega-sql diff main..HEAD models/
# Recursive directory scan
lexega-sql diff main..HEAD models/ -r
# Output as JSON for CI
lexega-sql diff main..HEAD models/ --format json
# With policy enforcement
lexega-sql diff main..HEAD models/ --policy policy.yaml
# Compare specific files
lexega-sql diff main..HEAD file1.sql file2.sql
# Markdown output for PR comments
lexega-sql diff main..HEAD models/ --format markdown
# Report downstream columns broken by a removed column (cross-script blast radius)
lexega-sql diff main..HEAD models/ --impact
Code Review (Git Integration)
# Review recent commits
lexega-sql review HEAD~10..HEAD
# Review with output directory
lexega-sql review HEAD~5..HEAD --report-out /tmp/reports
# Review with minimum severity filter
lexega-sql review HEAD~1..HEAD --min-severity high
# Markdown format for PR automation
lexega-sql review HEAD~1..HEAD --format markdown
# Post review as PR comment (GitHub, GitLab, Bitbucket)
lexega-sql review HEAD~1..HEAD --pr-comment
Default format:
reviewdefaults to--format markdown(unlikeanalyzeanddiffwhich default totext). Use--format textor--format jsonto override.
The --pr-comment flag automatically detects your CI platform and posts the review as a PR/MR comment. Supported platforms:
| Platform | Detection | Required Variables |
|---|---|---|
| GitHub Actions | GITHUB_ACTIONS=true | GITHUB_TOKEN |
| GitLab CI | GITLAB_CI=true | GITLAB_TOKEN or CI_JOB_TOKEN |
| Bitbucket Pipelines | BITBUCKET_PIPELINE_UUID | BITBUCKET_TOKEN |
If run outside CI, falls back to stdout output with a warning.
Risk Analysis
# Analyze single file
lexega-sql analyze file.sql
# Analyze with catalog metadata
lexega-sql analyze --catalog catalog.json file.sql
# Analyze stdin
cat file.sql | lexega-sql analyze --stdin
# Output as JSON
lexega-sql analyze --format json -q file.sql > report.json
# Filter signals by minimum severity
lexega-sql analyze --min-severity critical file.sql
# With custom rules
lexega-sql analyze --custom-rules rules.yaml file.sql
# With policy enforcement (exit code 2 if blocked)
lexega-sql analyze --policy policy.yaml --env prod file.sql
# Disable all built-in rules (custom rules only)
lexega-sql analyze --no-builtin --custom-rules rules.yaml file.sql
# Batch mode: scan directory recursively
lexega-sql analyze . -r
# Batch mode with severity filter
lexega-sql analyze . -r --min-severity high
# Scan Python files and notebooks for embedded SQL
lexega-sql analyze --scan-embedded --dialect databricks notebooks/
# Cross-script: analyze a directory of .sql files as a connected set (producer-first)
lexega-sql analyze --cross-script -r scripts/
# Cross-script + catalog: flag classified columns leaving via COPY INTO across scripts
lexega-sql analyze --catalog catalog.json --cross-script -r scripts/
# Deployment variables: resolve ${VAR} markers before analysis so findings and policy see the real names
lexega-sql analyze --var ELT_DB=ELT deploy.sql
lexega-sql analyze --var-env ELT_DB deploy.sql # read ELT_DB from the environment (allowlisted)
lexega-sql analyze --var-syntax dollar-paren deploy.sql # also recognize sqlcmd $(VAR)
# SnowSQL scripts: resolve &var client variables before analysis (from --var or a config file)
lexega-sql analyze --snowsql-config ~/.snowsql/config script.sql
Additional Flags
| Flag | Description |
|---|---|
--strict [<MODE>] | Fail on unparsed/unsupported statements. Modes: off (default), strict, pedantic. Bare --strict = strict. Also available on diff. Settable via LEXEGA_STRICT. |
--verbose | Per-statement rule-evaluation trace including matched paths and rejection reasons. More granular than --explain-signals. |
--report-artifact-format <FMT> | Format for written findings artifacts (risk reports, batch summaries, diff/review reports — i.e. anything --report-out produces): json (default), yaml, or sarif. SARIF is the standard schema for findings, suitable for CI integration (GitHub Security tab, etc.). Independent of --format, which controls stdout. |
--decision-artifact-format <FMT> | Format for written decision artifacts (policy verdicts produced via --decision-out): json (default) or yaml. SARIF is intentionally not offered — SARIF is a findings schema and has no representation for a policy decision. |
--provider <NAME> | Catalog provider override when using --catalog: snowflake, postgresql, bigquery, databricks. |
Variable Substitution
Deployment tooling often rewrites SQL before it runs (envsubst-style ${NAME}, sqlcmd's $(NAME), or a homegrown scheme) so the file in the repo is not the SQL the warehouse executes. Lexega resolves these markers first, so findings name the real objects and a policy that gates (for example) the production database matches the resolved name. This runs on analyze, diff, review, ci, and mcp; fmt never substitutes.
Values come from, in priority order:
| Flag | Description |
|---|---|
--var NAME=VALUE | Set one value directly (repeatable). |
--var-file <FILE> | Load values from a file. |
--var-env NAME | Read NAME from the environment (repeatable). An explicit allowlist — nothing is read from the environment without being named — so CI jobs that already export the deploy values need no duplication. |
Syntax. ${name} is recognized by default. sqlcmd's $(name) is enabled with --var-syntax dollar-paren. Per-repo settings live in .lexega.toml:
[template.substitution]
presets = ["dollar-brace", "dollar-paren"] # omit for the default (["dollar-brace"]); [] disables recognition
env = ["ELT_DB", "STAGE"] # environment allowlist, same as --var-env
[[template.substitution.custom]] # a homegrown delimiter pair
prefix = "%%"
suffix = "%%"
A repository whose SQL legitimately contains ${…} for another reason can set presets = [] to turn recognition off entirely.
Unresolved markers don't break analysis. A marker with no configured value is analyzed as its logical name (USE ${ELT_DB} → USE ELT_DB), with a warning listing the unresolved variables and lowered render completeness and confidence in the report. A resolved value substitutes everywhere, including inside string literals; an unresolved marker inside a string literal is left as written, and markers in comments are ignored.
This composes with SnowSQL client-variable substitution (&var, from --var or --snowsql-config): deployment markers resolve first, then SnowSQL variables.
Embedded SQL Extraction
Lexega can extract and analyze SQL embedded in Python files, Jupyter notebooks, and Databricks notebooks. This is useful for teams using PySpark, SQLAlchemy, or notebook-based workflows.
# Scan Python files for spark.sql(), cursor.execute(), etc.
lexega-sql analyze --scan-embedded --dialect databricks *.py
# Scan Jupyter notebooks for %%sql magic cells
lexega-sql analyze --scan-embedded --dialect databricks *.ipynb
# Scan a directory recursively
lexega-sql analyze --scan-embedded -r --dialect databricks notebooks/
# Use with policy enforcement
lexega-sql analyze --scan-embedded --policy policy.yaml --dialect databricks .
Supported file types:
| File Type | Patterns Extracted |
|---|---|
.py files | spark.sql("..."), cursor.execute("..."), session.sql("..."), and similar |
.ipynb (Jupyter) | %%sql and %sql magic cells |
.py (Databricks) | # MAGIC %sql cells in Databricks notebook format |
How it works:
- Python files are parsed using a full AST parser (not regex)—accurate extraction even with multiline strings, f-strings, and variable assignments
- Constant propagation tracks
query = "SELECT ..."; spark.sql(query) - F-strings are extracted as skeletons:
f"SELECT {col} FROM t"→SELECT __INTERP_1__ FROM t(interpolations become placeholders) - Each extracted SQL fragment is analyzed independently with full risk detection
Dashboard (Visualization)
# Start dashboard (reads .lexega/ by default — works out of the box after `init`)
lexega-sql dashboard
# Explicit local directory
lexega-sql dashboard --data-dir .lexega/
# Read from cloud storage
lexega-sql dashboard --data-dir s3://my-bucket/lexega-data
# Custom port, don't auto-open browser
lexega-sql dashboard --data-dir .lexega/ --port 8080 --no-open
# Custom bind address (e.g. expose on network)
lexega-sql dashboard --data-dir .lexega/ --host 0.0.0.0 --port 3000
# Decouple SQLite cache from data dir (for read-only mounts or containers)
lexega-sql dashboard --data-dir /readonly/governance --db-path /tmp/dashboard.db
The data directory must contain decisions/ and reports/ subdirectories. Reports are ingested in JSON, YAML, and SARIF form — including the baseline.sarif that init writes. See Integration Options for the expected structure and cloud storage details.
MCP Server (AI Agents)
# Serve the check_sql tool over stdio for MCP clients (Claude Code, Cursor, ...)
lexega-sql mcp --policy policy.yaml --env prod
# With a default dialect and custom decision directory
lexega-sql mcp --policy policy.yaml --env prod --dialect postgresql --decision-out /var/lexega/decisions
# In a dbt project: render context for model checks
lexega-sql mcp --policy policy.yaml --env prod --dbt-project . --var environment=prod
The agent submits SQL through the check_sql tool and receives an allow/block decision; every check writes a decision artifact (default .lexega/decisions). Policy and environment are fixed at startup. dbt/Jinja templates are rendered before analysis (use --no-render to refuse them); see Runtime / Agent Integration.
Format
# Format file (implicit format command)
lexega-sql fmt file.sql
# Format in-place
lexega-sql fmt -w file.sql
# Format stdin to stdout
cat file.sql | lexega-sql fmt --stdin
# Format with specific style
lexega-sql fmt --style compact file.sql
# Check mode (exit 0 if no changes needed)
lexega-sql fmt --check file.sql
# Verify safety without writing
lexega-sql fmt --verify-only file.sql
Dialect Selection
Lexega defaults to Snowflake dialect. Use --dialect for other dialects:
# Format PostgreSQL
lexega-sql fmt --dialect postgresql query.sql
# Analyze PostgreSQL
lexega-sql analyze --dialect postgresql query.sql
# Diff PostgreSQL files
lexega-sql diff main..HEAD models/ -r --dialect postgresql
# Review PostgreSQL
lexega-sql review main..HEAD models/ -r --dialect postgresql
# Analyze BigQuery
lexega-sql analyze --dialect bigquery query.sql
# Analyze MySQL
lexega-sql analyze --dialect mysql query.sql
# Analyze MSSQL (SQL Server)
lexega-sql analyze --dialect mssql query.sql
# Analyze Databricks
lexega-sql analyze --dialect databricks query.sql
# Analyze Amazon Redshift
lexega-sql analyze --dialect redshift query.sql
Supported values: snowflake (default), postgresql, bigquery, mysql, mssql, databricks, redshift.
Catalog Management
# Pull catalog from Snowflake (requires sidecar)
lexega-sql catalog pull --out catalog.json
# Inspect catalog file
lexega-sql catalog inspect catalog.json
# Diff two catalogs
lexega-sql catalog diff old.json new.json
Policy Management
# Generate starter policy from a risk report
lexega-sql policy init --from-report report.json
# Specify action (allow/warn/block) and environment
lexega-sql policy init --from-report report.json --action warn --env prod
Policy Lint
# Validate policy file
lexega-sql policy-lint policy.yaml
# Also validate exceptions file
lexega-sql policy-lint policy.yaml --exceptions exceptions.yaml
# Treat warnings as errors (for CI)
lexega-sql policy-lint policy.yaml --strict
License Management
# Check license status
lexega-sql license status
# Activate license
lexega-sql license activate <KEY>
# Remove license
lexega-sql license remove
Environment Variables
| Variable | Description |
|---|---|
LEXEGA_LICENSE_KEY | License key for CI/CD (checked before file on disk) |
LEXEGA_CI | Set to 1 for strict mode (policy block = exit code 2) |
LEXEGA_STRICT | Default --strict mode for analyze and diff: off, strict, or pedantic |
LEXEGA_CTX_DB | Session database for table qualification (e.g., MYDB) |
LEXEGA_CTX_SCHEMA | Session schema for table qualification (e.g., PUBLIC) |
GITHUB_TOKEN | For --pr-comment on GitHub Actions |
GITLAB_TOKEN | For --pr-comment on GitLab CI |
BITBUCKET_TOKEN | For --pr-comment on Bitbucket Pipelines |
SYSTEM_ACCESSTOKEN | For --pr-comment on Azure DevOps (map $(System.AccessToken) onto the step) |
Tip: The
--dialectflag is available on all commands (fmt,analyze,diff,review). Default issnowflake. Use--dialect postgresql,--dialect bigquery,--dialect mysql,--dialect mssql,--dialect databricks, or--dialect redshiftas needed.
CI/CD License Setup:
Store your license key as a secret and pass it via environment variable:
# GitHub Actions
env:
LEXEGA_LICENSE_KEY: ${{ secrets.LEXEGA_LICENSE_KEY }}
# GitLab CI
variables:
LEXEGA_LICENSE_KEY: $LEXEGA_LICENSE_KEY
# Azure DevOps
env:
LEXEGA_LICENSE_KEY: $(LEXEGA_LICENSE_KEY)
Exit Codes
0: Success1: Parse error, CLI argument error, file I/O error, or verification failure2: Policy blocked (when using--policyand the policy returnsblock)
Note: Exit code 2 is for explicit policy blocks. The policy layer is the only way to fail CI — use
--policywith a policy bundle. SetLEXEGA_CI=1to require--policyin CI environments (prevents accidental bypass).
Need Help?
Can't find what you're looking for? Check out our GitHub or reach out to support.