qualia-framework 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/bin/cli.js +519 -0
- package/framework/agents/architecture-strategist.md +53 -0
- package/framework/agents/backend-agent.md +150 -0
- package/framework/agents/code-simplicity-reviewer.md +86 -0
- package/framework/agents/frontend-agent.md +111 -0
- package/framework/agents/kieran-typescript-reviewer.md +96 -0
- package/framework/agents/performance-oracle.md +111 -0
- package/framework/agents/qualia-codebase-mapper.md +760 -0
- package/framework/agents/qualia-debugger.md +1203 -0
- package/framework/agents/qualia-executor.md +881 -0
- package/framework/agents/qualia-integration-checker.md +423 -0
- package/framework/agents/qualia-phase-researcher.md +453 -0
- package/framework/agents/qualia-plan-checker.md +699 -0
- package/framework/agents/qualia-planner.md +1241 -0
- package/framework/agents/qualia-project-researcher.md +602 -0
- package/framework/agents/qualia-research-synthesizer.md +236 -0
- package/framework/agents/qualia-roadmapper.md +605 -0
- package/framework/agents/qualia-verifier.md +685 -0
- package/framework/agents/team-orchestrator.md +228 -0
- package/framework/agents/teams/full-stack-team.md +48 -0
- package/framework/agents/teams/optimize-team.md +53 -0
- package/framework/agents/teams/review-team.md +62 -0
- package/framework/agents/teams/ship-team.md +86 -0
- package/framework/agents/test-agent.md +182 -0
- package/framework/askpass.sh +2 -0
- package/framework/commands/design.md +53 -0
- package/framework/commands/quick-db.md +22 -0
- package/framework/config/retention.json +35 -0
- package/framework/core/PRINCIPLES.md +77 -0
- package/framework/hooks/auto-format.sh +45 -0
- package/framework/hooks/block-env-edit.sh +42 -0
- package/framework/hooks/branch-guard.sh +46 -0
- package/framework/hooks/confirm-delete.sh +56 -0
- package/framework/hooks/migration-validate.sh +68 -0
- package/framework/hooks/notification-speak.sh +15 -0
- package/framework/hooks/pre-commit.sh +80 -0
- package/framework/hooks/pre-compact.sh +55 -0
- package/framework/hooks/pre-deploy-gate.sh +151 -0
- package/framework/hooks/qualia-colors.sh +32 -0
- package/framework/hooks/retention-cleanup.sh +43 -0
- package/framework/hooks/save-session-state.sh +153 -0
- package/framework/hooks/session-context-loader.sh +28 -0
- package/framework/hooks/session-learn.sh +30 -0
- package/framework/knowledge/claudecode-bible.md +1384 -0
- package/framework/knowledge/client-prefs.md +22 -0
- package/framework/knowledge/common-fixes.md +25 -0
- package/framework/knowledge/deployment-map.md +35 -0
- package/framework/knowledge/email-signature.html +1 -0
- package/framework/knowledge/employees.md +8 -0
- package/framework/knowledge/learned-patterns.md +51 -0
- package/framework/knowledge/optimization-research-2026.md +137 -0
- package/framework/knowledge/qualia-context.md +67 -0
- package/framework/knowledge/supabase-patterns.md +50 -0
- package/framework/knowledge/voice-agent-patterns.md +46 -0
- package/framework/qualia-engine/VERSION +1 -0
- package/framework/qualia-engine/bin/qualia-tools.js +2160 -0
- package/framework/qualia-engine/bin/qualia-tools.test.js +1054 -0
- package/framework/qualia-engine/references/checkpoints.md +775 -0
- package/framework/qualia-engine/references/continuation-format.md +249 -0
- package/framework/qualia-engine/references/decimal-phase-calculation.md +65 -0
- package/framework/qualia-engine/references/design-quality.md +56 -0
- package/framework/qualia-engine/references/git-integration.md +254 -0
- package/framework/qualia-engine/references/git-planning-commit.md +50 -0
- package/framework/qualia-engine/references/model-profile-resolution.md +32 -0
- package/framework/qualia-engine/references/model-profiles.md +73 -0
- package/framework/qualia-engine/references/phase-argument-parsing.md +61 -0
- package/framework/qualia-engine/references/planning-config.md +195 -0
- package/framework/qualia-engine/references/questioning.md +141 -0
- package/framework/qualia-engine/references/tdd.md +263 -0
- package/framework/qualia-engine/references/ui-brand.md +160 -0
- package/framework/qualia-engine/references/verification-patterns.md +612 -0
- package/framework/qualia-engine/templates/DEBUG.md +159 -0
- package/framework/qualia-engine/templates/DESIGN.md +81 -0
- package/framework/qualia-engine/templates/UAT.md +247 -0
- package/framework/qualia-engine/templates/codebase/architecture.md +255 -0
- package/framework/qualia-engine/templates/codebase/concerns.md +310 -0
- package/framework/qualia-engine/templates/codebase/conventions.md +307 -0
- package/framework/qualia-engine/templates/codebase/integrations.md +280 -0
- package/framework/qualia-engine/templates/codebase/stack.md +186 -0
- package/framework/qualia-engine/templates/codebase/structure.md +285 -0
- package/framework/qualia-engine/templates/codebase/testing.md +480 -0
- package/framework/qualia-engine/templates/config.json +35 -0
- package/framework/qualia-engine/templates/context.md +283 -0
- package/framework/qualia-engine/templates/continue-here.md +78 -0
- package/framework/qualia-engine/templates/debug-subagent-prompt.md +91 -0
- package/framework/qualia-engine/templates/discovery.md +146 -0
- package/framework/qualia-engine/templates/milestone-archive.md +123 -0
- package/framework/qualia-engine/templates/milestone.md +115 -0
- package/framework/qualia-engine/templates/phase-prompt.md +567 -0
- package/framework/qualia-engine/templates/planner-subagent-prompt.md +117 -0
- package/framework/qualia-engine/templates/project.md +184 -0
- package/framework/qualia-engine/templates/projects/ai-agent.md +156 -0
- package/framework/qualia-engine/templates/projects/mobile-app.md +181 -0
- package/framework/qualia-engine/templates/projects/voice-agent.md +134 -0
- package/framework/qualia-engine/templates/projects/website.md +137 -0
- package/framework/qualia-engine/templates/requirements.md +231 -0
- package/framework/qualia-engine/templates/research-project/ARCHITECTURE.md +204 -0
- package/framework/qualia-engine/templates/research-project/FEATURES.md +147 -0
- package/framework/qualia-engine/templates/research-project/PITFALLS.md +200 -0
- package/framework/qualia-engine/templates/research-project/STACK.md +120 -0
- package/framework/qualia-engine/templates/research-project/SUMMARY.md +170 -0
- package/framework/qualia-engine/templates/research.md +552 -0
- package/framework/qualia-engine/templates/roadmap.md +202 -0
- package/framework/qualia-engine/templates/state.md +176 -0
- package/framework/qualia-engine/templates/summary-complex.md +59 -0
- package/framework/qualia-engine/templates/summary-minimal.md +41 -0
- package/framework/qualia-engine/templates/summary-standard.md +48 -0
- package/framework/qualia-engine/templates/summary.md +246 -0
- package/framework/qualia-engine/templates/user-setup.md +311 -0
- package/framework/qualia-engine/templates/verification-report.md +322 -0
- package/framework/qualia-engine/workflows/add-phase.md +179 -0
- package/framework/qualia-engine/workflows/add-todo.md +157 -0
- package/framework/qualia-engine/workflows/audit-milestone.md +241 -0
- package/framework/qualia-engine/workflows/check-todos.md +176 -0
- package/framework/qualia-engine/workflows/complete-milestone.md +858 -0
- package/framework/qualia-engine/workflows/diagnose-issues.md +219 -0
- package/framework/qualia-engine/workflows/discovery-phase.md +289 -0
- package/framework/qualia-engine/workflows/discuss-phase.md +534 -0
- package/framework/qualia-engine/workflows/execute-phase.md +559 -0
- package/framework/qualia-engine/workflows/execute-plan.md +438 -0
- package/framework/qualia-engine/workflows/help.md +470 -0
- package/framework/qualia-engine/workflows/insert-phase.md +220 -0
- package/framework/qualia-engine/workflows/list-phase-assumptions.md +178 -0
- package/framework/qualia-engine/workflows/map-codebase.md +327 -0
- package/framework/qualia-engine/workflows/new-milestone.md +363 -0
- package/framework/qualia-engine/workflows/new-project.md +1037 -0
- package/framework/qualia-engine/workflows/pause-work.md +122 -0
- package/framework/qualia-engine/workflows/plan-milestone-gaps.md +256 -0
- package/framework/qualia-engine/workflows/plan-phase.md +422 -0
- package/framework/qualia-engine/workflows/progress.md +354 -0
- package/framework/qualia-engine/workflows/quick.md +252 -0
- package/framework/qualia-engine/workflows/remove-phase.md +326 -0
- package/framework/qualia-engine/workflows/research-phase.md +74 -0
- package/framework/qualia-engine/workflows/resume-project.md +306 -0
- package/framework/qualia-engine/workflows/set-profile.md +80 -0
- package/framework/qualia-engine/workflows/settings.md +145 -0
- package/framework/qualia-engine/workflows/transition.md +556 -0
- package/framework/qualia-engine/workflows/update.md +197 -0
- package/framework/qualia-engine/workflows/verify-phase.md +195 -0
- package/framework/qualia-engine/workflows/verify-work.md +625 -0
- package/framework/rules/context7.md +11 -0
- package/framework/rules/deployment.md +29 -0
- package/framework/rules/frontend.md +33 -0
- package/framework/rules/security.md +12 -0
- package/framework/rules/speed.md +20 -0
- package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
- package/framework/scripts/apply-retention.sh +120 -0
- package/framework/scripts/bootstrap-pop-os.sh +354 -0
- package/framework/scripts/claude-voice +13 -0
- package/framework/scripts/cleanup.sh +131 -0
- package/framework/scripts/cowork-mode.sh +141 -0
- package/framework/scripts/generate-project-claude-md.sh +153 -0
- package/framework/scripts/load-test-webhook.js +172 -0
- package/framework/scripts/say.py +236 -0
- package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +167 -0
- package/framework/scripts/showcase-video-recorder/playwright-helpers.js +216 -0
- package/framework/scripts/speak.py +55 -0
- package/framework/scripts/speak.sh +18 -0
- package/framework/scripts/status.sh +138 -0
- package/framework/scripts/sync-to-framework.sh +65 -0
- package/framework/scripts/voice-hotkey.py +227 -0
- package/framework/scripts/voice-input.sh +51 -0
- package/framework/skills/animate/SKILL.md +202 -0
- package/framework/skills/bolder/SKILL.md +144 -0
- package/framework/skills/browser-qa/SKILL.md +536 -0
- package/framework/skills/clarify/SKILL.md +179 -0
- package/framework/skills/colorize/SKILL.md +170 -0
- package/framework/skills/critique/SKILL.md +126 -0
- package/framework/skills/deep-research/SKILL.md +271 -0
- package/framework/skills/delight/SKILL.md +329 -0
- package/framework/skills/deploy/SKILL.md +261 -0
- package/framework/skills/deploy-verify/SKILL.md +377 -0
- package/framework/skills/deploy-verify/scripts/canary-check.sh +206 -0
- package/framework/skills/deploy-verify/scripts/check-console-errors.js +147 -0
- package/framework/skills/deploy-verify/scripts/check-cwv.js +139 -0
- package/framework/skills/deploy-verify/scripts/project-detect.sh +84 -0
- package/framework/skills/deploy-verify/scripts/verify.sh +548 -0
- package/framework/skills/design-quieter/SKILL.md +130 -0
- package/framework/skills/distill/SKILL.md +149 -0
- package/framework/skills/docs-lookup/SKILL.md +78 -0
- package/framework/skills/fcm-notifications/SKILL.md +125 -0
- package/framework/skills/financial-ledger/SKILL.md +1039 -0
- package/framework/skills/frontend-master/NOTICE.md +4 -0
- package/framework/skills/frontend-master/SKILL.md +127 -0
- package/framework/skills/frontend-master/reference/color-and-contrast.md +132 -0
- package/framework/skills/frontend-master/reference/interaction-design.md +123 -0
- package/framework/skills/frontend-master/reference/motion-design.md +99 -0
- package/framework/skills/frontend-master/reference/responsive-design.md +114 -0
- package/framework/skills/frontend-master/reference/spatial-design.md +100 -0
- package/framework/skills/frontend-master/reference/typography.md +131 -0
- package/framework/skills/frontend-master/reference/ux-writing.md +107 -0
- package/framework/skills/harden/SKILL.md +357 -0
- package/framework/skills/i18n-rtl/SKILL.md +752 -0
- package/framework/skills/learn/SKILL.md +71 -0
- package/framework/skills/memory/SKILL.md +50 -0
- package/framework/skills/mobile-expo/SKILL.md +864 -0
- package/framework/skills/mobile-expo/references/store-checklist.md +550 -0
- package/framework/skills/nestjs-backend/README.md +73 -0
- package/framework/skills/nestjs-backend/SKILL.md +446 -0
- package/framework/skills/nestjs-backend/references/templates.md +1173 -0
- package/framework/skills/normalize/SKILL.md +79 -0
- package/framework/skills/onboard/SKILL.md +242 -0
- package/framework/skills/polish/SKILL.md +209 -0
- package/framework/skills/pr/SKILL.md +66 -0
- package/framework/skills/qualia/SKILL.md +153 -0
- package/framework/skills/qualia-add-todo/SKILL.md +68 -0
- package/framework/skills/qualia-audit-milestone/SKILL.md +92 -0
- package/framework/skills/qualia-check-todos/SKILL.md +55 -0
- package/framework/skills/qualia-complete-milestone/SKILL.md +108 -0
- package/framework/skills/qualia-debug/SKILL.md +149 -0
- package/framework/skills/qualia-design/SKILL.md +203 -0
- package/framework/skills/qualia-discuss-phase/SKILL.md +72 -0
- package/framework/skills/qualia-execute-phase/SKILL.md +86 -0
- package/framework/skills/qualia-help/SKILL.md +67 -0
- package/framework/skills/qualia-idk/SKILL.md +352 -0
- package/framework/skills/qualia-list-phase-assumptions/SKILL.md +67 -0
- package/framework/skills/qualia-new-milestone/SKILL.md +72 -0
- package/framework/skills/qualia-new-project/SKILL.md +92 -0
- package/framework/skills/qualia-optimize/SKILL.md +417 -0
- package/framework/skills/qualia-pause-work/SKILL.md +96 -0
- package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +57 -0
- package/framework/skills/qualia-plan-phase/SKILL.md +101 -0
- package/framework/skills/qualia-progress/SKILL.md +53 -0
- package/framework/skills/qualia-quick/SKILL.md +89 -0
- package/framework/skills/qualia-research-phase/SKILL.md +88 -0
- package/framework/skills/qualia-resume-work/SKILL.md +62 -0
- package/framework/skills/qualia-review/SKILL.md +263 -0
- package/framework/skills/qualia-start/SKILL.md +182 -0
- package/framework/skills/qualia-verify-work/SKILL.md +105 -0
- package/framework/skills/qualia-workflow/SKILL.md +130 -0
- package/framework/skills/rag/SKILL.md +750 -0
- package/framework/skills/responsive/SKILL.md +231 -0
- package/framework/skills/retro/SKILL.md +284 -0
- package/framework/skills/sakani-conventions/SKILL.md +136 -0
- package/framework/skills/sakani-conventions/evals/evals.json +23 -0
- package/framework/skills/sakani-conventions/references/entities.md +365 -0
- package/framework/skills/sakani-conventions/references/error-codes.md +95 -0
- package/framework/skills/seo-master/SKILL.md +490 -0
- package/framework/skills/seo-master/references/checklist.md +199 -0
- package/framework/skills/seo-master/references/structured-data.md +609 -0
- package/framework/skills/ship/SKILL.md +202 -0
- package/framework/skills/stack-researcher/SKILL.md +215 -0
- package/framework/skills/status/SKILL.md +154 -0
- package/framework/skills/status/scripts/health-check.sh +562 -0
- package/framework/skills/subscription-payments/SKILL.md +250 -0
- package/framework/skills/supabase/SKILL.md +973 -0
- package/framework/skills/supabase/references/templates.md +159 -0
- package/framework/skills/team/SKILL.md +67 -0
- package/framework/skills/test-runner/SKILL.md +202 -0
- package/framework/skills/voice-agent/SKILL.md +407 -0
- package/framework/skills/zoho-workflow/SKILL.md +51 -0
- package/framework/statusline-command.sh +117 -0
- package/package.json +24 -0
- package/profiles/fawzi.json +16 -0
- package/profiles/hasan.json +16 -0
- package/profiles/moayad.json +16 -0
- package/templates/CLAUDE-owner.md +52 -0
- package/templates/CLAUDE.md.hbs +58 -0
- package/templates/env.claude.template +12 -0
- package/templates/settings.json +141 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# canary-check.sh — Post-deploy canary health check
|
|
3
|
+
# Compares live metrics against stored baseline, flags regressions
|
|
4
|
+
# after 2 consecutive failures.
|
|
5
|
+
#
|
|
6
|
+
# Usage: canary-check.sh <URL> [--project-dir /path] [--update-baseline]
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
|
|
12
|
+
# ─── Parse args ──────────────────────────────────────────────
|
|
13
|
+
URL=""
|
|
14
|
+
PROJECT_DIR=""
|
|
15
|
+
UPDATE_BASELINE=false
|
|
16
|
+
|
|
17
|
+
while [[ $# -gt 0 ]]; do
|
|
18
|
+
case "$1" in
|
|
19
|
+
--project-dir) PROJECT_DIR="$2"; shift 2 ;;
|
|
20
|
+
--update-baseline) UPDATE_BASELINE=true; shift ;;
|
|
21
|
+
-*) echo "Unknown option: $1"; exit 1 ;;
|
|
22
|
+
*) URL="${1%/}"; shift ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
if [[ -z "$URL" ]]; then
|
|
27
|
+
echo '{"status":"FAIL","detail":"No URL provided"}'
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
PROJECT_DIR="${PROJECT_DIR:-$(pwd)}"
|
|
32
|
+
BASELINE_FILE="$PROJECT_DIR/.planning/canary-baseline.json"
|
|
33
|
+
|
|
34
|
+
# Failure tracking file keyed by URL hash
|
|
35
|
+
URL_HASH=$(echo -n "$URL" | md5sum | cut -d' ' -f1)
|
|
36
|
+
FAILURE_FILE="/tmp/.canary-failures-${URL_HASH}"
|
|
37
|
+
|
|
38
|
+
# ─── Colors ──────────────────────────────────────────────────
|
|
39
|
+
C_RESET="\033[0m"
|
|
40
|
+
C_GREEN="\033[32m"
|
|
41
|
+
C_YELLOW="\033[33m"
|
|
42
|
+
C_RED="\033[31m"
|
|
43
|
+
C_BOLD="\033[1m"
|
|
44
|
+
C_DIM="\033[2m"
|
|
45
|
+
|
|
46
|
+
# ─── Collect current metrics ────────────────────────────────
|
|
47
|
+
# Metric 1: HTTP status
|
|
48
|
+
CURRENT_HTTP=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 15 "$URL" 2>/dev/null || echo "000")
|
|
49
|
+
|
|
50
|
+
# Metric 2: Page load time (median of 3 measurements, in ms)
|
|
51
|
+
LOAD_TIMES=()
|
|
52
|
+
for i in 1 2 3; do
|
|
53
|
+
t=$(curl -sL -o /dev/null -w "%{time_total}" --max-time 15 "$URL" 2>/dev/null || echo "99")
|
|
54
|
+
ms=$(echo "$t * 1000" | bc 2>/dev/null | cut -d. -f1)
|
|
55
|
+
LOAD_TIMES+=("${ms:-9999}")
|
|
56
|
+
done
|
|
57
|
+
IFS=$'\n' SORTED_TIMES=($(sort -n <<<"${LOAD_TIMES[*]}")); unset IFS
|
|
58
|
+
CURRENT_LOAD_MS=${SORTED_TIMES[1]}
|
|
59
|
+
|
|
60
|
+
# Metric 3: Console error count (reuse existing check-console-errors.js)
|
|
61
|
+
CONSOLE_RESULT=$(node "$SCRIPT_DIR/check-console-errors.js" "$URL" 2>/dev/null || echo '{"status":"SKIP","errors":[]}')
|
|
62
|
+
CURRENT_CONSOLE_ERRORS=$(echo "$CONSOLE_RESULT" | jq '.errors | length' 2>/dev/null || echo "0")
|
|
63
|
+
|
|
64
|
+
# ─── Update baseline mode ───────────────────────────────────
|
|
65
|
+
if $UPDATE_BASELINE; then
|
|
66
|
+
mkdir -p "$(dirname "$BASELINE_FILE")"
|
|
67
|
+
cat > "$BASELINE_FILE" <<BEOF
|
|
68
|
+
{
|
|
69
|
+
"url": "$URL",
|
|
70
|
+
"captured_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
71
|
+
"http_status": $CURRENT_HTTP,
|
|
72
|
+
"console_error_count": $CURRENT_CONSOLE_ERRORS,
|
|
73
|
+
"load_time_ms": $CURRENT_LOAD_MS,
|
|
74
|
+
"consecutive_failures": 0
|
|
75
|
+
}
|
|
76
|
+
BEOF
|
|
77
|
+
echo -e "${C_GREEN}Canary baseline saved to $BASELINE_FILE${C_RESET}"
|
|
78
|
+
echo -e " HTTP: $CURRENT_HTTP | Load: ${CURRENT_LOAD_MS}ms | Console errors: $CURRENT_CONSOLE_ERRORS"
|
|
79
|
+
# Reset failure counter
|
|
80
|
+
rm -f "$FAILURE_FILE"
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# ─── Load baseline ──────────────────────────────────────────
|
|
85
|
+
if [[ ! -f "$BASELINE_FILE" ]]; then
|
|
86
|
+
echo -e "${C_YELLOW}No canary baseline found at $BASELINE_FILE${C_RESET}"
|
|
87
|
+
echo -e "${C_YELLOW}Creating initial baseline from current metrics...${C_RESET}"
|
|
88
|
+
# Auto-create baseline on first run
|
|
89
|
+
mkdir -p "$(dirname "$BASELINE_FILE")"
|
|
90
|
+
cat > "$BASELINE_FILE" <<BEOF
|
|
91
|
+
{
|
|
92
|
+
"url": "$URL",
|
|
93
|
+
"captured_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
94
|
+
"http_status": $CURRENT_HTTP,
|
|
95
|
+
"console_error_count": $CURRENT_CONSOLE_ERRORS,
|
|
96
|
+
"load_time_ms": $CURRENT_LOAD_MS,
|
|
97
|
+
"consecutive_failures": 0
|
|
98
|
+
}
|
|
99
|
+
BEOF
|
|
100
|
+
echo -e "${C_GREEN}Baseline created. Next canary run will compare against this.${C_RESET}"
|
|
101
|
+
rm -f "$FAILURE_FILE"
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
BASELINE_HTTP=$(jq -r '.http_status' "$BASELINE_FILE")
|
|
106
|
+
BASELINE_CONSOLE=$(jq -r '.console_error_count' "$BASELINE_FILE")
|
|
107
|
+
BASELINE_LOAD=$(jq -r '.load_time_ms' "$BASELINE_FILE")
|
|
108
|
+
|
|
109
|
+
# ─── Compare against baseline (relative thresholds) ─────────
|
|
110
|
+
REGRESSIONS=()
|
|
111
|
+
LOAD_THRESHOLD=$((BASELINE_LOAD * 2))
|
|
112
|
+
|
|
113
|
+
# Check 1: HTTP status mismatch
|
|
114
|
+
if [[ "$CURRENT_HTTP" != "$BASELINE_HTTP" ]]; then
|
|
115
|
+
REGRESSIONS+=("HTTP status: baseline=${BASELINE_HTTP}, current=${CURRENT_HTTP}")
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Check 2: Console errors increased (any new errors vs baseline count)
|
|
119
|
+
if [[ "$CURRENT_CONSOLE_ERRORS" -gt "$BASELINE_CONSOLE" ]]; then
|
|
120
|
+
NEW_ERRORS=$((CURRENT_CONSOLE_ERRORS - BASELINE_CONSOLE))
|
|
121
|
+
REGRESSIONS+=("Console errors: baseline=${BASELINE_CONSOLE}, current=${CURRENT_CONSOLE_ERRORS} (+${NEW_ERRORS} new)")
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# Check 3: Load time exceeds 2x baseline
|
|
125
|
+
if [[ "$CURRENT_LOAD_MS" -gt "$LOAD_THRESHOLD" ]]; then
|
|
126
|
+
REGRESSIONS+=("Load time: baseline=${BASELINE_LOAD}ms, current=${CURRENT_LOAD_MS}ms (threshold: ${LOAD_THRESHOLD}ms)")
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# ─── Consecutive failure tracking ────────────────────────────
|
|
130
|
+
PREV_FAILURES=0
|
|
131
|
+
if [[ -f "$FAILURE_FILE" ]]; then
|
|
132
|
+
PREV_FAILURES=$(cat "$FAILURE_FILE" 2>/dev/null || echo "0")
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# ─── Output ──────────────────────────────────────────────────
|
|
136
|
+
echo ""
|
|
137
|
+
echo -e "${C_BOLD}──────────────────────────────────────────────────────${C_RESET}"
|
|
138
|
+
echo -e " ${C_BOLD}CANARY CHECK${C_RESET} — T+2min post-deploy"
|
|
139
|
+
echo -e " URL: ${URL}"
|
|
140
|
+
echo -e " Baseline: $(jq -r '.captured_at' "$BASELINE_FILE")"
|
|
141
|
+
echo -e "${C_BOLD}──────────────────────────────────────────────────────${C_RESET}"
|
|
142
|
+
echo ""
|
|
143
|
+
|
|
144
|
+
# Metric comparison table
|
|
145
|
+
printf " %-20s %-12s %-12s %-12s\n" "Metric" "Baseline" "Current" "Status"
|
|
146
|
+
printf " %-20s %-12s %-12s %-12s\n" "────────────────────" "────────────" "────────────" "────────────"
|
|
147
|
+
|
|
148
|
+
# HTTP row
|
|
149
|
+
if [[ "$CURRENT_HTTP" == "$BASELINE_HTTP" ]]; then
|
|
150
|
+
printf " %-20s %-12s %-12s ${C_GREEN}%-12s${C_RESET}\n" "HTTP Status" "$BASELINE_HTTP" "$CURRENT_HTTP" "OK"
|
|
151
|
+
else
|
|
152
|
+
printf " %-20s %-12s %-12s ${C_RED}%-12s${C_RESET}\n" "HTTP Status" "$BASELINE_HTTP" "$CURRENT_HTTP" "REGRESSION"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Console errors row
|
|
156
|
+
if [[ "$CURRENT_CONSOLE_ERRORS" -le "$BASELINE_CONSOLE" ]]; then
|
|
157
|
+
printf " %-20s %-12s %-12s ${C_GREEN}%-12s${C_RESET}\n" "Console Errors" "$BASELINE_CONSOLE" "$CURRENT_CONSOLE_ERRORS" "OK"
|
|
158
|
+
else
|
|
159
|
+
printf " %-20s %-12s %-12s ${C_RED}%-12s${C_RESET}\n" "Console Errors" "$BASELINE_CONSOLE" "$CURRENT_CONSOLE_ERRORS" "REGRESSION"
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Load time row
|
|
163
|
+
if [[ "$CURRENT_LOAD_MS" -le "$LOAD_THRESHOLD" ]]; then
|
|
164
|
+
printf " %-20s %-12s %-12s ${C_GREEN}%-12s${C_RESET}\n" "Load Time (ms)" "$BASELINE_LOAD" "$CURRENT_LOAD_MS" "OK (<${LOAD_THRESHOLD}ms)"
|
|
165
|
+
else
|
|
166
|
+
printf " %-20s %-12s %-12s ${C_RED}%-12s${C_RESET}\n" "Load Time (ms)" "$BASELINE_LOAD" "$CURRENT_LOAD_MS" "REGRESSION (>${LOAD_THRESHOLD}ms)"
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
echo ""
|
|
170
|
+
|
|
171
|
+
if [[ ${#REGRESSIONS[@]} -eq 0 ]]; then
|
|
172
|
+
# All good — reset failure counter
|
|
173
|
+
echo "0" > "$FAILURE_FILE"
|
|
174
|
+
echo -e " ${C_GREEN}CANARY PASS${C_RESET} — All metrics within baseline thresholds"
|
|
175
|
+
echo ""
|
|
176
|
+
exit 0
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Regressions detected — increment failure counter
|
|
180
|
+
CONSECUTIVE=$((PREV_FAILURES + 1))
|
|
181
|
+
echo "$CONSECUTIVE" > "$FAILURE_FILE"
|
|
182
|
+
|
|
183
|
+
if [[ "$CONSECUTIVE" -ge 2 ]]; then
|
|
184
|
+
# 2+ consecutive failures — FLAG REGRESSION
|
|
185
|
+
echo -e " ${C_RED}${C_BOLD}CANARY ALERT — REGRESSION DETECTED${C_RESET}"
|
|
186
|
+
echo -e " ${C_RED}Consecutive failures: ${CONSECUTIVE}${C_RESET}"
|
|
187
|
+
echo ""
|
|
188
|
+
for reg in "${REGRESSIONS[@]}"; do
|
|
189
|
+
echo -e " ${C_RED} - ${reg}${C_RESET}"
|
|
190
|
+
done
|
|
191
|
+
echo ""
|
|
192
|
+
echo -e " ${C_DIM}Action: Investigate regressions. If false alarm, update baseline:${C_RESET}"
|
|
193
|
+
echo -e " ${C_DIM} bash $SCRIPT_DIR/canary-check.sh $URL --update-baseline${C_RESET}"
|
|
194
|
+
echo ""
|
|
195
|
+
exit 1
|
|
196
|
+
else
|
|
197
|
+
# First failure — note but do NOT alert
|
|
198
|
+
echo -e " ${C_YELLOW}CANARY WARN — Regression detected (1st occurrence, not alerting)${C_RESET}"
|
|
199
|
+
echo -e " ${C_YELLOW}Will alert if next canary also fails.${C_RESET}"
|
|
200
|
+
echo ""
|
|
201
|
+
for reg in "${REGRESSIONS[@]}"; do
|
|
202
|
+
echo -e " ${C_YELLOW} - ${reg}${C_RESET}"
|
|
203
|
+
done
|
|
204
|
+
echo ""
|
|
205
|
+
exit 0
|
|
206
|
+
fi
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// check-console-errors.js — Detect client-side JS errors via Playwright headless browser
|
|
3
|
+
// Usage: node check-console-errors.js <URL>
|
|
4
|
+
// Output: JSON { "status": "PASS|WARN|FAIL", "errors": [...], "detail": "..." }
|
|
5
|
+
|
|
6
|
+
const url = process.argv[2];
|
|
7
|
+
if (!url) {
|
|
8
|
+
console.log(JSON.stringify({ status: "FAIL", errors: [], detail: "No URL provided" }));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Known benign error patterns (third-party, browser extensions, etc.)
|
|
13
|
+
const BENIGN_PATTERNS = [
|
|
14
|
+
/third.?party/i,
|
|
15
|
+
/gtag/i,
|
|
16
|
+
/analytics/i,
|
|
17
|
+
/fbq/i,
|
|
18
|
+
/hotjar/i,
|
|
19
|
+
/intercom/i,
|
|
20
|
+
/clarity/i,
|
|
21
|
+
/sentry/i,
|
|
22
|
+
/Failed to load resource.*favicon/i,
|
|
23
|
+
/ERR_BLOCKED_BY_CLIENT/i, // ad blockers
|
|
24
|
+
/net::ERR_FAILED.*chrome-extension/i,
|
|
25
|
+
/ResizeObserver loop/i,
|
|
26
|
+
/Loading chunk.*failed/i, // sometimes transient
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// Critical error patterns — these always mean FAIL
|
|
30
|
+
const CRITICAL_PATTERNS = [
|
|
31
|
+
/TypeError/,
|
|
32
|
+
/ReferenceError/,
|
|
33
|
+
/SyntaxError/,
|
|
34
|
+
/ChunkLoadError/,
|
|
35
|
+
/Unhandled/i,
|
|
36
|
+
/NEXT_/,
|
|
37
|
+
/Internal Server Error/i,
|
|
38
|
+
/500/,
|
|
39
|
+
/Cannot read propert/i,
|
|
40
|
+
/is not defined/i,
|
|
41
|
+
/is not a function/i,
|
|
42
|
+
/Hydration/i,
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
async function run() {
|
|
46
|
+
let chromium;
|
|
47
|
+
try {
|
|
48
|
+
// Try to import playwright
|
|
49
|
+
const pw = require('playwright');
|
|
50
|
+
chromium = pw.chromium;
|
|
51
|
+
} catch {
|
|
52
|
+
try {
|
|
53
|
+
const pw = require('playwright-core');
|
|
54
|
+
chromium = pw.chromium;
|
|
55
|
+
} catch {
|
|
56
|
+
console.log(JSON.stringify({
|
|
57
|
+
status: "SKIP",
|
|
58
|
+
errors: [],
|
|
59
|
+
detail: "Playwright not available — install with: npm i -g playwright"
|
|
60
|
+
}));
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const errors = [];
|
|
66
|
+
const warnings = [];
|
|
67
|
+
let browser;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
browser = await chromium.launch({
|
|
71
|
+
headless: true,
|
|
72
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
73
|
+
});
|
|
74
|
+
const page = await browser.newPage();
|
|
75
|
+
|
|
76
|
+
// Collect console errors
|
|
77
|
+
page.on('console', msg => {
|
|
78
|
+
if (msg.type() === 'error') {
|
|
79
|
+
errors.push(msg.text());
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Collect page-level errors (uncaught exceptions)
|
|
84
|
+
page.on('pageerror', err => {
|
|
85
|
+
errors.push(err.message || String(err));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Navigate with timeout
|
|
89
|
+
await page.goto(url, {
|
|
90
|
+
waitUntil: 'networkidle',
|
|
91
|
+
timeout: 30000,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Give a brief extra moment for any late errors
|
|
95
|
+
await page.waitForTimeout(2000);
|
|
96
|
+
|
|
97
|
+
await browser.close();
|
|
98
|
+
browser = null;
|
|
99
|
+
|
|
100
|
+
// Classify errors
|
|
101
|
+
const criticalErrors = [];
|
|
102
|
+
const benignErrors = [];
|
|
103
|
+
|
|
104
|
+
for (const err of errors) {
|
|
105
|
+
const isBenign = BENIGN_PATTERNS.some(p => p.test(err));
|
|
106
|
+
const isCritical = CRITICAL_PATTERNS.some(p => p.test(err));
|
|
107
|
+
|
|
108
|
+
if (isCritical && !isBenign) {
|
|
109
|
+
criticalErrors.push(err);
|
|
110
|
+
} else {
|
|
111
|
+
benignErrors.push(err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (criticalErrors.length > 0) {
|
|
116
|
+
console.log(JSON.stringify({
|
|
117
|
+
status: "FAIL",
|
|
118
|
+
errors: criticalErrors,
|
|
119
|
+
benign: benignErrors.length,
|
|
120
|
+
detail: `${criticalErrors.length} critical error(s): ${criticalErrors[0].substring(0, 120)}`,
|
|
121
|
+
}));
|
|
122
|
+
} else if (benignErrors.length > 0) {
|
|
123
|
+
console.log(JSON.stringify({
|
|
124
|
+
status: "WARN",
|
|
125
|
+
errors: benignErrors,
|
|
126
|
+
benign: benignErrors.length,
|
|
127
|
+
detail: `${benignErrors.length} non-critical error(s) (third-party/benign)`,
|
|
128
|
+
}));
|
|
129
|
+
} else {
|
|
130
|
+
console.log(JSON.stringify({
|
|
131
|
+
status: "PASS",
|
|
132
|
+
errors: [],
|
|
133
|
+
detail: "No console errors detected",
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (browser) await browser.close().catch(() => {});
|
|
138
|
+
console.log(JSON.stringify({
|
|
139
|
+
status: "FAIL",
|
|
140
|
+
errors: [err.message],
|
|
141
|
+
detail: `Browser check failed: ${err.message.substring(0, 120)}`,
|
|
142
|
+
}));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
run();
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// check-cwv.js — Core Web Vitals via Google PageSpeed Insights API
|
|
3
|
+
// Usage: node check-cwv.js <URL>
|
|
4
|
+
// Output: JSON { "status": "PASS|WARN|FAIL|SKIP", "metrics": {...}, "detail": "..." }
|
|
5
|
+
//
|
|
6
|
+
// Thresholds (Google standards):
|
|
7
|
+
// LCP: < 2.5s PASS, 2.5-4s WARN, > 4s FAIL
|
|
8
|
+
// CLS: < 0.1 PASS, 0.1-0.25 WARN, > 0.25 FAIL
|
|
9
|
+
// INP: < 200ms PASS, 200-500ms WARN, > 500ms FAIL
|
|
10
|
+
|
|
11
|
+
const url = process.argv[2];
|
|
12
|
+
if (!url) {
|
|
13
|
+
console.log(JSON.stringify({ status: "FAIL", metrics: {}, detail: "No URL provided" }));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const THRESHOLDS = {
|
|
18
|
+
LCP: { good: 2500, poor: 4000, unit: "ms", label: "Largest Contentful Paint" },
|
|
19
|
+
CLS: { good: 0.1, poor: 0.25, unit: "", label: "Cumulative Layout Shift" },
|
|
20
|
+
INP: { good: 200, poor: 500, unit: "ms", label: "Interaction to Next Paint" },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function classify(metric, value) {
|
|
24
|
+
const t = THRESHOLDS[metric];
|
|
25
|
+
if (value <= t.good) return "PASS";
|
|
26
|
+
if (value <= t.poor) return "WARN";
|
|
27
|
+
return "FAIL";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function formatValue(metric, value) {
|
|
31
|
+
const t = THRESHOLDS[metric];
|
|
32
|
+
if (metric === "CLS") return value.toFixed(2);
|
|
33
|
+
if (metric === "LCP") return `${(value / 1000).toFixed(1)}s`;
|
|
34
|
+
return `${Math.round(value)}${t.unit}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function run() {
|
|
38
|
+
const apiUrl = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&strategy=mobile&category=performance`;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetch(apiUrl, { signal: AbortSignal.timeout(60000) });
|
|
42
|
+
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
console.log(JSON.stringify({
|
|
45
|
+
status: "SKIP",
|
|
46
|
+
metrics: {},
|
|
47
|
+
detail: `PageSpeed API returned ${response.status} — skipping CWV check`,
|
|
48
|
+
}));
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
|
|
54
|
+
// Extract metrics from field data (CrUX) first, fall back to lab data
|
|
55
|
+
const metrics = {};
|
|
56
|
+
let worstStatus = "PASS";
|
|
57
|
+
|
|
58
|
+
// Try CrUX (field) data first — more representative of real users
|
|
59
|
+
const fieldMetrics = data.loadingExperience?.metrics || {};
|
|
60
|
+
|
|
61
|
+
// LCP
|
|
62
|
+
if (fieldMetrics.LARGEST_CONTENTFUL_PAINT_MS) {
|
|
63
|
+
metrics.LCP = fieldMetrics.LARGEST_CONTENTFUL_PAINT_MS.percentile;
|
|
64
|
+
} else {
|
|
65
|
+
// Fall back to Lighthouse lab data
|
|
66
|
+
const lcpAudit = data.lighthouseResult?.audits?.['largest-contentful-paint'];
|
|
67
|
+
if (lcpAudit) metrics.LCP = lcpAudit.numericValue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// CLS
|
|
71
|
+
if (fieldMetrics.CUMULATIVE_LAYOUT_SHIFT_SCORE) {
|
|
72
|
+
metrics.CLS = fieldMetrics.CUMULATIVE_LAYOUT_SHIFT_SCORE.percentile / 100;
|
|
73
|
+
} else {
|
|
74
|
+
const clsAudit = data.lighthouseResult?.audits?.['cumulative-layout-shift'];
|
|
75
|
+
if (clsAudit) metrics.CLS = clsAudit.numericValue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// INP
|
|
79
|
+
if (fieldMetrics.INTERACTION_TO_NEXT_PAINT) {
|
|
80
|
+
metrics.INP = fieldMetrics.INTERACTION_TO_NEXT_PAINT.percentile;
|
|
81
|
+
} else if (fieldMetrics.EXPERIMENTAL_INTERACTION_TO_NEXT_PAINT) {
|
|
82
|
+
metrics.INP = fieldMetrics.EXPERIMENTAL_INTERACTION_TO_NEXT_PAINT.percentile;
|
|
83
|
+
}
|
|
84
|
+
// INP has no Lighthouse lab equivalent — skip if no field data
|
|
85
|
+
|
|
86
|
+
// Build result
|
|
87
|
+
const results = {};
|
|
88
|
+
const parts = [];
|
|
89
|
+
|
|
90
|
+
for (const [metric, value] of Object.entries(metrics)) {
|
|
91
|
+
const status = classify(metric, value);
|
|
92
|
+
const formatted = formatValue(metric, value);
|
|
93
|
+
const icon = status === "PASS" ? "✓" : status === "WARN" ? "⚠" : "✗";
|
|
94
|
+
results[metric] = { value, formatted, status };
|
|
95
|
+
parts.push(`${metric}: ${formatted} ${icon}`);
|
|
96
|
+
|
|
97
|
+
// Track worst status
|
|
98
|
+
if (status === "FAIL") worstStatus = "FAIL";
|
|
99
|
+
else if (status === "WARN" && worstStatus !== "FAIL") worstStatus = "WARN";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (Object.keys(metrics).length === 0) {
|
|
103
|
+
console.log(JSON.stringify({
|
|
104
|
+
status: "SKIP",
|
|
105
|
+
metrics: {},
|
|
106
|
+
detail: "No CWV data available (site may lack sufficient traffic for CrUX data)",
|
|
107
|
+
}));
|
|
108
|
+
} else {
|
|
109
|
+
// Also include overall performance score
|
|
110
|
+
const perfScore = data.lighthouseResult?.categories?.performance?.score;
|
|
111
|
+
if (perfScore !== undefined) {
|
|
112
|
+
results.performanceScore = Math.round(perfScore * 100);
|
|
113
|
+
parts.push(`Score: ${results.performanceScore}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log(JSON.stringify({
|
|
117
|
+
status: worstStatus,
|
|
118
|
+
metrics: results,
|
|
119
|
+
detail: parts.join(" "),
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
if (err.name === 'TimeoutError' || err.code === 'ABORT_ERR') {
|
|
124
|
+
console.log(JSON.stringify({
|
|
125
|
+
status: "SKIP",
|
|
126
|
+
metrics: {},
|
|
127
|
+
detail: "PageSpeed API timed out (60s) — skipping CWV check",
|
|
128
|
+
}));
|
|
129
|
+
} else {
|
|
130
|
+
console.log(JSON.stringify({
|
|
131
|
+
status: "SKIP",
|
|
132
|
+
metrics: {},
|
|
133
|
+
detail: `PageSpeed API error: ${err.message?.substring(0, 100)}`,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
run();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# project-detect.sh — Auto-detect project from current directory
|
|
3
|
+
# Returns JSON: {"name":"...","supabase_ref":"...","hosting":"..."}
|
|
4
|
+
#
|
|
5
|
+
# Usage: source project-detect.sh (sets PROJECT_NAME, SUPABASE_REF, HOSTING)
|
|
6
|
+
# or: bash project-detect.sh (prints JSON)
|
|
7
|
+
|
|
8
|
+
CONTEXT_FILE="$HOME/.claude/knowledge/qualia-context.md"
|
|
9
|
+
CWD="${1:-$(pwd)}"
|
|
10
|
+
DIR_NAME=$(basename "$CWD")
|
|
11
|
+
|
|
12
|
+
# Normalize common directory structures
|
|
13
|
+
# ~/Projects/Live-Projects/vero → vero
|
|
14
|
+
# ~/Projects/aiagents/sofiatesting → sofiatesting
|
|
15
|
+
# ~/Projects/voice/under → under
|
|
16
|
+
# ~/Projects/websites/qualia → qualia (ERP)
|
|
17
|
+
PROJECT_NAME="$DIR_NAME"
|
|
18
|
+
|
|
19
|
+
# Known project → Supabase ref mapping (from qualia-context.md)
|
|
20
|
+
declare -A SUPABASE_REFS=(
|
|
21
|
+
["aquador"]="hznpuxplqgszbacxzbhv"
|
|
22
|
+
["maud"]="vspyxscitcuwnrhchskl"
|
|
23
|
+
["vero"]="zskfdlqyzhkzefafqkpx"
|
|
24
|
+
["aibossbrainz"]="esymbjpzjjkffpfqukxw"
|
|
25
|
+
["mpm"]="llherorsfgbdyqkrrlpc"
|
|
26
|
+
["Zaid"]="uoqiwidqlsoamtugioik"
|
|
27
|
+
["dababneh"]="toovladeybmlyzblqtbl"
|
|
28
|
+
["faris"]="lrgwxcqqnhxvvrruepcv"
|
|
29
|
+
["Hammah"]="ctcikdcjhxxqogwvtzrc"
|
|
30
|
+
["sofiatesting"]="vceeheaxcrhmpqueudqx"
|
|
31
|
+
["timo"]="qgaqvkmqterlljbegniu"
|
|
32
|
+
["under"]="vvppyijxpgeeijiozlve"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Known project → hosting mapping
|
|
36
|
+
declare -A HOSTING_MAP=(
|
|
37
|
+
["armenius"]="cloudflare"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
SUPABASE_REF="${SUPABASE_REFS[$PROJECT_NAME]:-}"
|
|
41
|
+
HOSTING="${HOSTING_MAP[$PROJECT_NAME]:-vercel}"
|
|
42
|
+
|
|
43
|
+
# Also try to read from .env.local if no hardcoded ref
|
|
44
|
+
if [[ -z "$SUPABASE_REF" ]]; then
|
|
45
|
+
for env_file in "$CWD/.env.local" "$CWD/.env"; do
|
|
46
|
+
if [[ -f "$env_file" ]]; then
|
|
47
|
+
# Extract ref from NEXT_PUBLIC_SUPABASE_URL=https://XXX.supabase.co
|
|
48
|
+
ref=$(grep -oP 'NEXT_PUBLIC_SUPABASE_URL=https://\K[^.]+' "$env_file" 2>/dev/null)
|
|
49
|
+
if [[ -n "$ref" ]]; then
|
|
50
|
+
SUPABASE_REF="$ref"
|
|
51
|
+
break
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Detect external dependencies from env files
|
|
58
|
+
DEPS=""
|
|
59
|
+
for env_file in "$CWD/.env.local" "$CWD/.env"; do
|
|
60
|
+
if [[ -f "$env_file" ]]; then
|
|
61
|
+
# Read variable names only (not values) — security safe
|
|
62
|
+
grep -oP '^[A-Z_]+(?==)' "$env_file" 2>/dev/null | while read -r var; do
|
|
63
|
+
case "$var" in
|
|
64
|
+
VAPI_*|NEXT_PUBLIC_VAPI_*) echo "vapi" ;;
|
|
65
|
+
OPENROUTER_*) echo "openrouter" ;;
|
|
66
|
+
ELEVEN_LABS_*|ELEVENLABS_*) echo "elevenlabs" ;;
|
|
67
|
+
RETELL_*) echo "retell" ;;
|
|
68
|
+
STRIPE_*|NEXT_PUBLIC_STRIPE_*) echo "stripe" ;;
|
|
69
|
+
TELNYX_*) echo "telnyx" ;;
|
|
70
|
+
esac
|
|
71
|
+
done | sort -u
|
|
72
|
+
break
|
|
73
|
+
fi
|
|
74
|
+
done > /tmp/deploy-verify-deps 2>/dev/null
|
|
75
|
+
DEPS=$(cat /tmp/deploy-verify-deps 2>/dev/null | tr '\n' ',' | sed 's/,$//')
|
|
76
|
+
rm -f /tmp/deploy-verify-deps
|
|
77
|
+
|
|
78
|
+
# If sourced, export variables
|
|
79
|
+
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
|
80
|
+
export PROJECT_NAME SUPABASE_REF HOSTING DEPS
|
|
81
|
+
else
|
|
82
|
+
# If run directly, print JSON
|
|
83
|
+
echo "{\"name\":\"$PROJECT_NAME\",\"supabase_ref\":\"$SUPABASE_REF\",\"hosting\":\"$HOSTING\",\"deps\":\"$DEPS\"}"
|
|
84
|
+
fi
|