diff --git a/CLAUDE.md b/CLAUDE.md index cd2bbc832..046244d2a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,11 +27,11 @@ uv add --group dev package_name ### Database Access ```bash -scripts/supabase/tables.sh # List all tables (dev) -scripts/supabase/describe.sh users # Show columns for a table (dev) -scripts/supabase/query.sh "SELECT * FROM users LIMIT 1" # Run a query (dev, tabular) -scripts/supabase/query.sh -x "SELECT * FROM users LIMIT 1" # Vertical display -scripts/supabase/query.sh --prod "SELECT ..." # Run against production +scripts/supabase/tables.sh --dev # List all tables (dev) +scripts/supabase/describe.sh --dev users # Show columns for a table (dev) +scripts/supabase/query.sh --dev "SELECT * FROM users LIMIT 1" # Run a query (dev, tabular) +scripts/supabase/query.sh --dev -x "SELECT * FROM users LIMIT 1" # Vertical display +scripts/supabase/query.sh --prd "SELECT ..." # Run against production ``` ### Sentry CLI @@ -135,7 +135,7 @@ assert find_test_files("foo.ts", all_files, None) == ["foo.test.ts"] - Technical, descriptive title. **No `## Test plan`**. - **Two posts** (last section, customer-facing only): GitAuto (changelog) + Wes (personal voice, don't emphasize "GitAuto") - Format: `## Social Media Post (GitAuto)` and `## Social Media Post (Wes)` headers (parsed by `extract-social-posts.js`) - - **GitAuto post**: Changelog format — one-liner headline + change bullets. No storytelling. + - **GitAuto post**: Changelog format — one-liner headline + customer-facing feature bullets (no test/internal changes). Each feature on one line. Include items mentioned in the PR title. - **Wes post**: Honest stories. Vary openers — check recent posts first. - Guidelines: No em dashes (—). Under 280 chars. No marketing keywords. No negative framing. No internal names. No small numbers — use relative language. 8. If Sentry issue: `python3 scripts/sentry/get_issue.py AGENT-XXX` then `python3 scripts/sentry/resolve_issue.py AGENT-XXX ...` diff --git a/pyproject.toml b/pyproject.toml index 68787fded..c3a059b35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "GitAuto" -version = "1.6.10" +version = "1.6.11" requires-python = ">=3.14" dependencies = [ "annotated-doc==0.0.4", diff --git a/scripts/git/pre_commit_hook.sh b/scripts/git/pre_commit_hook.sh index b96652e4d..c36ca8fde 100755 --- a/scripts/git/pre_commit_hook.sh +++ b/scripts/git/pre_commit_hook.sh @@ -9,6 +9,13 @@ set -uo pipefail echo "=== Pre-commit hook ===" +# Block commits directly to main +BRANCH=$(git branch --show-current) +if [ "$BRANCH" = "main" ]; then + echo "FAILED: Cannot commit directly to main. Use a feature branch." + exit 1 +fi + # Keep local main in sync with remote (no checkout needed) git fetch origin main:main 2>/dev/null || true diff --git a/scripts/supabase/daily_cost_report.sh b/scripts/supabase/daily_cost_report.sh index f9146b01f..51e977082 100755 --- a/scripts/supabase/daily_cost_report.sh +++ b/scripts/supabase/daily_cost_report.sh @@ -1,19 +1,19 @@ #!/bin/bash -# Usage: scripts/supabase/daily_cost_report.sh [YYYY-MM-DD] [--prod] +# Usage: scripts/supabase/daily_cost_report.sh [YYYY-MM-DD] [--dev|--prd] # Defaults to today (PT timezone) and production database. -# Shows LLM cost breakdown by repo/PR/trigger/model with totals. +# Shows LLM cost breakdown grouped by repo/PR, with trigger/model detail. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" DATE="" -PROD_FLAG="--prod" +ENV_FLAG="--prd" while [[ $# -gt 0 ]]; do case $1 in - --dev) PROD_FLAG=""; shift ;; - --prod) PROD_FLAG="--prod"; shift ;; + --dev) ENV_FLAG="--dev"; shift ;; + --prod|--prd) ENV_FLAG="--prd"; shift ;; *) DATE="$1"; shift ;; esac done @@ -25,16 +25,16 @@ fi NEXT_DATE=$(date -j -f "%Y-%m-%d" -v+1d "$DATE" +%Y-%m-%d 2>/dev/null || date -d "$DATE + 1 day" +%Y-%m-%d) +WHERE="u.created_at >= '${DATE}T00:00:00-07:00' AND u.created_at < '${NEXT_DATE}T00:00:00-07:00'" + echo "=== Daily LLM Cost Report: $DATE (PT) ===" echo "" +echo "--- By PR (grouped) ---" -# Summary by repo/PR/trigger/model -"$SCRIPT_DIR/query.sh" $PROD_FLAG " +"$SCRIPT_DIR/query.sh" $ENV_FLAG " SELECT u.owner_name || '/' || u.repo_name AS repo, u.pr_number AS pr, - u.trigger, - REPLACE(REPLACE(REPLACE(lr.model_id, 'claude-', ''), '-4-6', '46'), '-4-5', '45') AS model, COUNT(lr.id) AS calls, SUM(lr.input_tokens) AS in_tok, SUM(lr.output_tokens) AS out_tok, @@ -43,18 +43,38 @@ SELECT ROUND(SUM(lr.total_cost_usd)::numeric, 2) AS total FROM usage u JOIN llm_requests lr ON lr.usage_id = u.id -WHERE u.created_at >= '${DATE}T00:00:00-07:00' - AND u.created_at < '${NEXT_DATE}T00:00:00-07:00' -GROUP BY u.owner_name, u.repo_name, u.pr_number, u.trigger, lr.model_id +WHERE $WHERE +GROUP BY u.owner_name, u.repo_name, u.pr_number ORDER BY total DESC " +echo "" +echo "--- Breakdown by trigger/model (within each PR) ---" + +"$SCRIPT_DIR/query.sh" $ENV_FLAG " +SELECT + u.owner_name || '/' || u.repo_name AS repo, + u.pr_number AS pr, + u.trigger, + REPLACE(REPLACE(REPLACE(lr.model_id, 'claude-', ''), '-4-6', '46'), '-4-5', '45') AS model, + COUNT(lr.id) AS calls, + ROUND(SUM(lr.input_cost_usd)::numeric, 2) AS in_cost, + ROUND(SUM(lr.output_cost_usd)::numeric, 2) AS out_cost, + ROUND(SUM(lr.total_cost_usd)::numeric, 2) AS total +FROM usage u +JOIN llm_requests lr ON lr.usage_id = u.id +WHERE $WHERE +GROUP BY u.owner_name, u.repo_name, u.pr_number, u.trigger, lr.model_id +ORDER BY u.owner_name, u.repo_name, u.pr_number, total DESC +" + echo "" echo "=== Totals ===" -"$SCRIPT_DIR/query.sh" $PROD_FLAG " +"$SCRIPT_DIR/query.sh" $ENV_FLAG " SELECT COUNT(DISTINCT u.id) AS runs, + COUNT(DISTINCT u.owner_name || '/' || u.repo_name || '#' || u.pr_number) AS prs, COUNT(lr.id) AS calls, SUM(lr.input_tokens) AS in_tokens, SUM(lr.output_tokens) AS out_tokens, @@ -63,18 +83,16 @@ SELECT ROUND(SUM(lr.total_cost_usd)::numeric, 2) AS total FROM usage u JOIN llm_requests lr ON lr.usage_id = u.id -WHERE u.created_at >= '${DATE}T00:00:00-07:00' - AND u.created_at < '${NEXT_DATE}T00:00:00-07:00' +WHERE $WHERE " echo "" -echo "=== Top 5 Most Expensive Runs (input growth pattern) ===" +echo "=== Top 5 Most Expensive PRs (input growth pattern) ===" -"$SCRIPT_DIR/query.sh" $PROD_FLAG " +"$SCRIPT_DIR/query.sh" $ENV_FLAG " SELECT u.owner_name || '/' || u.repo_name AS repo, u.pr_number AS pr, - u.trigger, COUNT(lr.id) AS calls, MIN(lr.input_length) AS min_in_len, MAX(lr.input_length) AS max_in_len, @@ -82,9 +100,8 @@ SELECT ROUND(SUM(lr.total_cost_usd)::numeric, 2) AS total FROM usage u JOIN llm_requests lr ON lr.usage_id = u.id -WHERE u.created_at >= '${DATE}T00:00:00-07:00' - AND u.created_at < '${NEXT_DATE}T00:00:00-07:00' -GROUP BY u.id, u.owner_name, u.repo_name, u.pr_number, u.trigger +WHERE $WHERE +GROUP BY u.owner_name, u.repo_name, u.pr_number ORDER BY total DESC LIMIT 5 " diff --git a/scripts/supabase/describe.sh b/scripts/supabase/describe.sh index 68b1981f0..5df93ca75 100755 --- a/scripts/supabase/describe.sh +++ b/scripts/supabase/describe.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: scripts/supabase/describe.sh [-p|--prod] -# Shows columns, types, and nullability for a table. +# Usage: scripts/supabase/describe.sh <--dev|--prd> +# Shows columns, types, and nullability for a table. Requires explicit --dev or --prd. set -euo pipefail @@ -9,24 +9,31 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" source "$PROJECT_ROOT/.env" -ENV="dev" +ENV="" TABLE="" while [[ $# -gt 0 ]]; do case $1 in - -p|--prod) ENV="prod"; shift ;; + -d|--dev) ENV="dev"; shift ;; + -p|--prd) ENV="prd"; shift ;; *) TABLE="$1"; shift ;; esac done +if [[ -z "$ENV" ]]; then + echo "Error: must specify --dev or --prd" + echo "Usage: $0 <--dev|--prd> " + exit 1 +fi + if [[ -z "$TABLE" ]]; then - echo "Usage: $0 [-p|--prod] " + echo "Usage: $0 <--dev|--prd> " exit 1 fi QUERY="SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '$TABLE' ORDER BY ordinal_position;" -if [[ "$ENV" == "prod" ]]; then +if [[ "$ENV" == "prd" ]]; then psql "postgresql://postgres.awegqusxzsmlgxaxyyrq:$SUPABASE_DB_PASSWORD_PRD@aws-0-us-west-1.pooler.supabase.com:6543/postgres" -c "$QUERY" else psql "postgresql://postgres.dkrxtcbaqzrodvsagwwn:$SUPABASE_DB_PASSWORD_DEV@aws-0-us-west-1.pooler.supabase.com:6543/postgres" -c "$QUERY" diff --git a/scripts/supabase/query.sh b/scripts/supabase/query.sh index 373d38486..af2f7a7b8 100755 --- a/scripts/supabase/query.sh +++ b/scripts/supabase/query.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: scripts/supabase/query.sh [-p|--prod] [-x] "SQL query" -# Defaults to development database. Use -x for vertical display. +# Usage: scripts/supabase/query.sh <--dev|--prd> [-x] "SQL query" +# Requires explicit --dev or --prd. Use -x for vertical display. set -euo pipefail @@ -9,25 +9,32 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" source "$PROJECT_ROOT/.env" -ENV="dev" +ENV="" QUERY="" PSQL_FLAGS="" while [[ $# -gt 0 ]]; do case $1 in - -p|--prod) ENV="prod"; shift ;; + -d|--dev) ENV="dev"; shift ;; + -p|--prd) ENV="prd"; shift ;; -x) PSQL_FLAGS="-x"; shift ;; *) QUERY="$1"; shift ;; esac done +if [[ -z "$ENV" ]]; then + echo "Error: must specify --dev or --prd" + echo "Usage: $0 <--dev|--prd> [-x] \"SQL query\"" + exit 1 +fi + if [[ -z "$QUERY" ]]; then - echo "Usage: $0 [-p|--prod] [-x] \"SQL query\"" + echo "Usage: $0 <--dev|--prd> [-x] \"SQL query\"" echo " -x Vertical display (expanded output)" exit 1 fi -if [[ "$ENV" == "prod" ]]; then +if [[ "$ENV" == "prd" ]]; then psql "postgresql://postgres.awegqusxzsmlgxaxyyrq:$SUPABASE_DB_PASSWORD_PRD@aws-0-us-west-1.pooler.supabase.com:6543/postgres" $PSQL_FLAGS -c "$QUERY" else psql "postgresql://postgres.dkrxtcbaqzrodvsagwwn:$SUPABASE_DB_PASSWORD_DEV@aws-0-us-west-1.pooler.supabase.com:6543/postgres" $PSQL_FLAGS -c "$QUERY" diff --git a/scripts/supabase/tables.sh b/scripts/supabase/tables.sh index ab94483bd..729b56a28 100755 --- a/scripts/supabase/tables.sh +++ b/scripts/supabase/tables.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: scripts/supabase/tables.sh [-p|--prod] -# Lists all tables in the public schema. +# Usage: scripts/supabase/tables.sh <--dev|--prd> +# Lists all tables in the public schema. Requires explicit --dev or --prd. set -euo pipefail @@ -9,18 +9,25 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" source "$PROJECT_ROOT/.env" -ENV="dev" +ENV="" while [[ $# -gt 0 ]]; do case $1 in - -p|--prod) ENV="prod"; shift ;; - *) echo "Usage: $0 [-p|--prod]"; exit 1 ;; + -d|--dev) ENV="dev"; shift ;; + -p|--prd) ENV="prd"; shift ;; + *) echo "Usage: $0 <--dev|--prd>"; exit 1 ;; esac done +if [[ -z "$ENV" ]]; then + echo "Error: must specify --dev or --prd" + echo "Usage: $0 <--dev|--prd>" + exit 1 +fi + QUERY="SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename;" -if [[ "$ENV" == "prod" ]]; then +if [[ "$ENV" == "prd" ]]; then psql "postgresql://postgres.awegqusxzsmlgxaxyyrq:$SUPABASE_DB_PASSWORD_PRD@aws-0-us-west-1.pooler.supabase.com:6543/postgres" -c "$QUERY" else psql "postgresql://postgres.dkrxtcbaqzrodvsagwwn:$SUPABASE_DB_PASSWORD_DEV@aws-0-us-west-1.pooler.supabase.com:6543/postgres" -c "$QUERY" diff --git a/uv.lock b/uv.lock index 59808db6b..eef20a740 100644 --- a/uv.lock +++ b/uv.lock @@ -596,7 +596,7 @@ wheels = [ [[package]] name = "gitauto" -version = "1.6.10" +version = "1.6.11" source = { virtual = "." } dependencies = [ { name = "annotated-doc" },