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,548 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# verify.sh — Production deploy verification (8 checks with retry logic)
|
|
3
|
+
# Usage: verify.sh <URL> [--project-dir /path/to/project]
|
|
4
|
+
#
|
|
5
|
+
# Checks: HTTP Status, Auth Flow, Console Errors, API Latency,
|
|
6
|
+
# DB Connectivity, Core Web Vitals, Dependencies, SSL Certificate
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# ─── Config ───────────────────────────────────────────────────────────
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
MAX_RETRIES=3
|
|
13
|
+
RETRY_DELAYS=(5 15 30) # Exponential backoff in seconds
|
|
14
|
+
LATENCY_PASS=500 # ms
|
|
15
|
+
LATENCY_WARN=1000 # ms
|
|
16
|
+
SSL_WARN_DAYS=30
|
|
17
|
+
SSL_FAIL_DAYS=7
|
|
18
|
+
DB_LATENCY_WARN=500 # ms
|
|
19
|
+
|
|
20
|
+
# ─── Parse args ───────────────────────────────────────────────────────
|
|
21
|
+
URL=""
|
|
22
|
+
PROJECT_DIR=""
|
|
23
|
+
|
|
24
|
+
while [[ $# -gt 0 ]]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--project-dir) PROJECT_DIR="$2"; shift 2 ;;
|
|
27
|
+
-*) echo "Unknown option: $1"; exit 1 ;;
|
|
28
|
+
*) URL="${1%/}"; shift ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
if [[ -z "$URL" ]]; then
|
|
33
|
+
echo "Usage: verify.sh <URL> [--project-dir /path/to/project]"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# ─── Project detection ────────────────────────────────────────────────
|
|
38
|
+
PROJECT_DIR="${PROJECT_DIR:-$(pwd)}"
|
|
39
|
+
source "$SCRIPT_DIR/project-detect.sh" "$PROJECT_DIR" 2>/dev/null || true
|
|
40
|
+
|
|
41
|
+
# Extract domain from URL
|
|
42
|
+
DOMAIN=$(echo "$URL" | sed -E 's|https?://([^/:]+).*|\1|')
|
|
43
|
+
|
|
44
|
+
# ─── State ────────────────────────────────────────────────────────────
|
|
45
|
+
PASS_COUNT=0
|
|
46
|
+
WARN_COUNT=0
|
|
47
|
+
FAIL_COUNT=0
|
|
48
|
+
SKIP_COUNT=0
|
|
49
|
+
CRITICAL_FAIL=false
|
|
50
|
+
START_TIME=$(date +%s%N)
|
|
51
|
+
RESULTS=()
|
|
52
|
+
|
|
53
|
+
# ─── Output helpers ───────────────────────────────────────────────────
|
|
54
|
+
icon_pass="✓" icon_warn="⚠" icon_fail="✗" icon_skip="○"
|
|
55
|
+
C_RESET="\033[0m"
|
|
56
|
+
C_GREEN="\033[32m"
|
|
57
|
+
C_YELLOW="\033[33m"
|
|
58
|
+
C_RED="\033[31m"
|
|
59
|
+
C_DIM="\033[2m"
|
|
60
|
+
C_BOLD="\033[1m"
|
|
61
|
+
|
|
62
|
+
record() {
|
|
63
|
+
local status="$1" name="$2" detail="$3"
|
|
64
|
+
local icon color
|
|
65
|
+
case "$status" in
|
|
66
|
+
PASS) icon="$icon_pass"; color="$C_GREEN"; ((PASS_COUNT++)) || true ;;
|
|
67
|
+
WARN) icon="$icon_warn"; color="$C_YELLOW"; ((WARN_COUNT++)) || true ;;
|
|
68
|
+
FAIL) icon="$icon_fail"; color="$C_RED"; ((FAIL_COUNT++)) || true ;;
|
|
69
|
+
SKIP) icon="$icon_skip"; color="$C_DIM"; ((SKIP_COUNT++)) || true ;;
|
|
70
|
+
esac
|
|
71
|
+
# Pad name to 20 chars
|
|
72
|
+
local padded
|
|
73
|
+
padded=$(printf "%-20s" "$name")
|
|
74
|
+
RESULTS+=("$(printf " ${color}${icon} %-4s${C_RESET} ${color}${padded}${C_RESET} ${detail}" "$status")")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
log_retry() {
|
|
78
|
+
local name="$1" attempt="$2" delay="$3"
|
|
79
|
+
echo -e " ${C_DIM}⟳ Retrying ${name} (attempt ${attempt}/${MAX_RETRIES}, waiting ${delay}s)...${C_RESET}"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# ─── Retry wrapper ────────────────────────────────────────────────────
|
|
83
|
+
# Usage: with_retry "Check Name" check_function
|
|
84
|
+
# The check_function should set CHECK_STATUS (PASS/WARN/FAIL) and CHECK_DETAIL
|
|
85
|
+
with_retry() {
|
|
86
|
+
local name="$1"
|
|
87
|
+
shift
|
|
88
|
+
local func="$1"
|
|
89
|
+
shift
|
|
90
|
+
|
|
91
|
+
for ((attempt=1; attempt<=MAX_RETRIES; attempt++)); do
|
|
92
|
+
CHECK_STATUS=""
|
|
93
|
+
CHECK_DETAIL=""
|
|
94
|
+
"$func" "$@" || true
|
|
95
|
+
|
|
96
|
+
if [[ "$CHECK_STATUS" == "PASS" || "$CHECK_STATUS" == "WARN" || "$CHECK_STATUS" == "SKIP" ]]; then
|
|
97
|
+
record "$CHECK_STATUS" "$name" "$CHECK_DETAIL"
|
|
98
|
+
return
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Failed — retry if not last attempt
|
|
102
|
+
if ((attempt < MAX_RETRIES)); then
|
|
103
|
+
local delay=${RETRY_DELAYS[$((attempt-1))]}
|
|
104
|
+
log_retry "$name" "$((attempt+1))" "$delay"
|
|
105
|
+
sleep "$delay"
|
|
106
|
+
fi
|
|
107
|
+
done
|
|
108
|
+
|
|
109
|
+
# All retries exhausted
|
|
110
|
+
record "FAIL" "$name" "$CHECK_DETAIL"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# ─── Check 1: HTTP Status (CRITICAL) ─────────────────────────────────
|
|
114
|
+
check_http() {
|
|
115
|
+
# Check root URL
|
|
116
|
+
local http_code response_time
|
|
117
|
+
http_code=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 15 "$URL" 2>/dev/null || echo "000")
|
|
118
|
+
response_time=$(curl -sL -o /dev/null -w "%{time_total}" --max-time 15 "$URL" 2>/dev/null || echo "0")
|
|
119
|
+
local ms
|
|
120
|
+
ms=$(echo "$response_time * 1000" | bc 2>/dev/null | cut -d. -f1)
|
|
121
|
+
ms=${ms:-0}
|
|
122
|
+
|
|
123
|
+
# Check /api/health if it exists
|
|
124
|
+
local health_code=""
|
|
125
|
+
health_code=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 10 "$URL/api/health" 2>/dev/null || echo "000")
|
|
126
|
+
|
|
127
|
+
local health_note=""
|
|
128
|
+
if [[ "$health_code" == "200" ]]; then
|
|
129
|
+
health_note=", /api/health: ${health_code}"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
if [[ "$http_code" == "200" ]]; then
|
|
133
|
+
CHECK_STATUS="PASS"
|
|
134
|
+
CHECK_DETAIL="${http_code} OK (${ms}ms)${health_note}"
|
|
135
|
+
elif [[ "$http_code" =~ ^3[0-9]{2}$ ]]; then
|
|
136
|
+
CHECK_STATUS="WARN"
|
|
137
|
+
CHECK_DETAIL="Redirect ${http_code} (may need to check final destination)"
|
|
138
|
+
elif [[ "$http_code" == "000" ]]; then
|
|
139
|
+
CHECK_STATUS="FAIL"
|
|
140
|
+
CHECK_DETAIL="Connection failed — timeout or DNS error"
|
|
141
|
+
CRITICAL_FAIL=true
|
|
142
|
+
else
|
|
143
|
+
CHECK_STATUS="FAIL"
|
|
144
|
+
CHECK_DETAIL="Got HTTP ${http_code} (expected 200)"
|
|
145
|
+
CRITICAL_FAIL=true
|
|
146
|
+
fi
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# ─── Check 2: Auth Flow ──────────────────────────────────────────────
|
|
150
|
+
check_auth() {
|
|
151
|
+
local found_auth=false
|
|
152
|
+
local auth_details=()
|
|
153
|
+
|
|
154
|
+
# Step 1: Check protected routes for redirect behavior
|
|
155
|
+
for route in "/dashboard" "/admin" "/app" "/account"; do
|
|
156
|
+
# Use -D to capture headers, don't follow redirects
|
|
157
|
+
local headers
|
|
158
|
+
headers=$(curl -sI --max-time 10 "$URL$route" 2>/dev/null || echo "")
|
|
159
|
+
local status_line
|
|
160
|
+
status_line=$(echo "$headers" | head -1)
|
|
161
|
+
|
|
162
|
+
if echo "$status_line" | grep -qE "30[1237]"; then
|
|
163
|
+
local location
|
|
164
|
+
location=$(echo "$headers" | grep -i "^location:" | head -1 | tr -d '\r' | awk '{print $2}')
|
|
165
|
+
if echo "$location" | grep -qiE "(login|auth|sign.?in)"; then
|
|
166
|
+
auth_details+=("Protected ${route} → redirect to login")
|
|
167
|
+
found_auth=true
|
|
168
|
+
break
|
|
169
|
+
fi
|
|
170
|
+
elif echo "$status_line" | grep -q "401"; then
|
|
171
|
+
auth_details+=("Protected ${route} → 401 Unauthorized")
|
|
172
|
+
found_auth=true
|
|
173
|
+
break
|
|
174
|
+
fi
|
|
175
|
+
done
|
|
176
|
+
|
|
177
|
+
# Step 2: Supabase auth health (if ref available)
|
|
178
|
+
if [[ -n "${SUPABASE_REF:-}" ]]; then
|
|
179
|
+
local auth_health
|
|
180
|
+
auth_health=$(curl -s --max-time 10 "https://${SUPABASE_REF}.supabase.co/auth/v1/health" 2>/dev/null || echo "")
|
|
181
|
+
local auth_status
|
|
182
|
+
auth_status=$(echo "$auth_health" | jq -r '.status' 2>/dev/null || echo "")
|
|
183
|
+
|
|
184
|
+
if [[ "$auth_status" == "ok" ]]; then
|
|
185
|
+
auth_details+=("Supabase auth healthy")
|
|
186
|
+
found_auth=true
|
|
187
|
+
elif [[ -n "$auth_health" ]]; then
|
|
188
|
+
CHECK_STATUS="FAIL"
|
|
189
|
+
CHECK_DETAIL="Supabase auth unhealthy: $auth_health"
|
|
190
|
+
return
|
|
191
|
+
fi
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Step 3: Check auth callback endpoints
|
|
195
|
+
for path in "/api/auth/callback" "/auth/callback" "/api/auth/session"; do
|
|
196
|
+
local cb_status
|
|
197
|
+
cb_status=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 10 "$URL$path" 2>/dev/null || echo "000")
|
|
198
|
+
if [[ "$cb_status" != "404" && "$cb_status" != "000" ]]; then
|
|
199
|
+
auth_details+=("${path} → ${cb_status}")
|
|
200
|
+
found_auth=true
|
|
201
|
+
break
|
|
202
|
+
fi
|
|
203
|
+
done
|
|
204
|
+
|
|
205
|
+
if $found_auth; then
|
|
206
|
+
CHECK_STATUS="PASS"
|
|
207
|
+
CHECK_DETAIL=$(IFS=", "; echo "${auth_details[*]}")
|
|
208
|
+
else
|
|
209
|
+
CHECK_STATUS="SKIP"
|
|
210
|
+
CHECK_DETAIL="No auth endpoints detected"
|
|
211
|
+
fi
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# ─── Check 3: Console Errors ─────────────────────────────────────────
|
|
215
|
+
check_console() {
|
|
216
|
+
local result
|
|
217
|
+
result=$(node "$SCRIPT_DIR/check-console-errors.js" "$URL" 2>/dev/null || echo '{"status":"SKIP","detail":"Playwright unavailable"}')
|
|
218
|
+
|
|
219
|
+
CHECK_STATUS=$(echo "$result" | jq -r '.status' 2>/dev/null || echo "SKIP")
|
|
220
|
+
CHECK_DETAIL=$(echo "$result" | jq -r '.detail' 2>/dev/null || echo "Could not parse result")
|
|
221
|
+
|
|
222
|
+
# Fallback if Playwright failed — do basic curl check
|
|
223
|
+
if [[ "$CHECK_STATUS" == "SKIP" ]]; then
|
|
224
|
+
local page
|
|
225
|
+
page=$(curl -sL --max-time 10 "$URL" 2>/dev/null || echo "")
|
|
226
|
+
if echo "$page" | grep -qi "internal server error\|500 error\|application error"; then
|
|
227
|
+
CHECK_STATUS="FAIL"
|
|
228
|
+
CHECK_DETAIL="Server error detected in page HTML (degraded check — Playwright unavailable)"
|
|
229
|
+
elif echo "$page" | grep -qi "<!DOCTYPE\|<html"; then
|
|
230
|
+
CHECK_STATUS="WARN"
|
|
231
|
+
CHECK_DETAIL="HTML OK but full browser check skipped (Playwright unavailable)"
|
|
232
|
+
else
|
|
233
|
+
CHECK_STATUS="FAIL"
|
|
234
|
+
CHECK_DETAIL="Response is not HTML (degraded check)"
|
|
235
|
+
fi
|
|
236
|
+
fi
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# ─── Check 4: API Latency ────────────────────────────────────────────
|
|
240
|
+
check_latency() {
|
|
241
|
+
local measurements=()
|
|
242
|
+
local endpoints=("$URL")
|
|
243
|
+
|
|
244
|
+
# Add /api/health if it responds
|
|
245
|
+
local health_check
|
|
246
|
+
health_check=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 5 "$URL/api/health" 2>/dev/null || echo "000")
|
|
247
|
+
if [[ "$health_check" == "200" ]]; then
|
|
248
|
+
endpoints+=("$URL/api/health")
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
local details=()
|
|
252
|
+
local worst_status="PASS"
|
|
253
|
+
|
|
254
|
+
for endpoint in "${endpoints[@]}"; do
|
|
255
|
+
local times=()
|
|
256
|
+
for i in 1 2 3; do
|
|
257
|
+
local t
|
|
258
|
+
t=$(curl -sL -o /dev/null -w "%{time_total}" --max-time 15 "$endpoint" 2>/dev/null || echo "99")
|
|
259
|
+
local ms
|
|
260
|
+
ms=$(echo "$t * 1000" | bc 2>/dev/null | cut -d. -f1)
|
|
261
|
+
times+=("${ms:-9999}")
|
|
262
|
+
done
|
|
263
|
+
|
|
264
|
+
# Sort and take median
|
|
265
|
+
IFS=$'\n' sorted=($(sort -n <<<"${times[*]}")); unset IFS
|
|
266
|
+
local median=${sorted[1]}
|
|
267
|
+
|
|
268
|
+
local label
|
|
269
|
+
label=$(echo "$endpoint" | sed "s|${URL}||")
|
|
270
|
+
label=${label:-"/"}
|
|
271
|
+
|
|
272
|
+
local status_icon
|
|
273
|
+
if ((median < LATENCY_PASS)); then
|
|
274
|
+
status_icon="✓"
|
|
275
|
+
# worst stays as is
|
|
276
|
+
elif ((median < LATENCY_WARN)); then
|
|
277
|
+
status_icon="⚠"
|
|
278
|
+
if [[ "$worst_status" == "PASS" ]]; then worst_status="WARN"; fi
|
|
279
|
+
else
|
|
280
|
+
status_icon="✗"
|
|
281
|
+
worst_status="FAIL"
|
|
282
|
+
fi
|
|
283
|
+
details+=("${label}: ${median}ms ${status_icon}")
|
|
284
|
+
done
|
|
285
|
+
|
|
286
|
+
CHECK_STATUS="$worst_status"
|
|
287
|
+
CHECK_DETAIL=$(IFS=", "; echo "${details[*]}")
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# ─── Check 5: Database Connectivity ──────────────────────────────────
|
|
291
|
+
check_db() {
|
|
292
|
+
if [[ -z "${SUPABASE_REF:-}" ]]; then
|
|
293
|
+
CHECK_STATUS="SKIP"
|
|
294
|
+
CHECK_DETAIL="No Supabase ref detected"
|
|
295
|
+
return
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
local start_ns end_ns
|
|
299
|
+
start_ns=$(date +%s%N)
|
|
300
|
+
|
|
301
|
+
# Try Supabase REST health endpoint first (doesn't need auth)
|
|
302
|
+
local rest_status
|
|
303
|
+
rest_status=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 10 \
|
|
304
|
+
"https://${SUPABASE_REF}.supabase.co/rest/v1/" \
|
|
305
|
+
-H "apikey: placeholder" 2>/dev/null || echo "000")
|
|
306
|
+
|
|
307
|
+
end_ns=$(date +%s%N)
|
|
308
|
+
local db_ms=$(( (end_ns - start_ns) / 1000000 ))
|
|
309
|
+
|
|
310
|
+
# REST endpoint returns 401 with invalid key but that means it's reachable
|
|
311
|
+
if [[ "$rest_status" =~ ^(200|401|403)$ ]]; then
|
|
312
|
+
if ((db_ms > DB_LATENCY_WARN)); then
|
|
313
|
+
CHECK_STATUS="WARN"
|
|
314
|
+
CHECK_DETAIL="Supabase reachable but slow (${db_ms}ms)"
|
|
315
|
+
else
|
|
316
|
+
CHECK_STATUS="PASS"
|
|
317
|
+
CHECK_DETAIL="Supabase responding (${db_ms}ms)"
|
|
318
|
+
fi
|
|
319
|
+
elif [[ "$rest_status" == "000" ]]; then
|
|
320
|
+
CHECK_STATUS="FAIL"
|
|
321
|
+
CHECK_DETAIL="Supabase unreachable (connection timeout)"
|
|
322
|
+
else
|
|
323
|
+
CHECK_STATUS="WARN"
|
|
324
|
+
CHECK_DETAIL="Supabase returned HTTP ${rest_status} (${db_ms}ms)"
|
|
325
|
+
fi
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# ─── Check 6: Core Web Vitals ────────────────────────────────────────
|
|
329
|
+
check_cwv() {
|
|
330
|
+
local result
|
|
331
|
+
result=$(node "$SCRIPT_DIR/check-cwv.js" "$URL" 2>/dev/null || echo '{"status":"SKIP","detail":"CWV check failed"}')
|
|
332
|
+
|
|
333
|
+
CHECK_STATUS=$(echo "$result" | jq -r '.status' 2>/dev/null || echo "SKIP")
|
|
334
|
+
CHECK_DETAIL=$(echo "$result" | jq -r '.detail' 2>/dev/null || echo "Could not run CWV check")
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
# ─── Check 7: Dependency Services ────────────────────────────────────
|
|
338
|
+
check_deps() {
|
|
339
|
+
# Status/health endpoints for known services
|
|
340
|
+
declare -A DEP_URLS=(
|
|
341
|
+
["stripe"]="https://status.stripe.com/api/v2/status.json"
|
|
342
|
+
["vapi"]="https://api.vapi.ai/health"
|
|
343
|
+
["openrouter"]="https://openrouter.ai/api/v1/models"
|
|
344
|
+
["elevenlabs"]="https://api.elevenlabs.io/v1/models"
|
|
345
|
+
["retell"]="https://api.retellai.com/v2/agent"
|
|
346
|
+
["telnyx"]="https://api.telnyx.com/v2/available_phone_numbers"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Detect deps from env files
|
|
350
|
+
local detected_deps=()
|
|
351
|
+
for env_file in "$PROJECT_DIR/.env.local" "$PROJECT_DIR/.env"; do
|
|
352
|
+
if [[ -f "$env_file" ]]; then
|
|
353
|
+
local vars
|
|
354
|
+
vars=$(grep -oP '^[A-Z_]+(?==)' "$env_file" 2>/dev/null || true)
|
|
355
|
+
for var in $vars; do
|
|
356
|
+
case "$var" in
|
|
357
|
+
VAPI_*|NEXT_PUBLIC_VAPI_*) detected_deps+=("vapi") ;;
|
|
358
|
+
OPENROUTER_*) detected_deps+=("openrouter") ;;
|
|
359
|
+
ELEVEN_LABS_*|ELEVENLABS_*) detected_deps+=("elevenlabs") ;;
|
|
360
|
+
RETELL_*) detected_deps+=("retell") ;;
|
|
361
|
+
STRIPE_*|NEXT_PUBLIC_STRIPE_*) detected_deps+=("stripe") ;;
|
|
362
|
+
TELNYX_*) detected_deps+=("telnyx") ;;
|
|
363
|
+
esac
|
|
364
|
+
done
|
|
365
|
+
break
|
|
366
|
+
fi
|
|
367
|
+
done
|
|
368
|
+
|
|
369
|
+
# Deduplicate
|
|
370
|
+
if [[ ${#detected_deps[@]} -gt 0 ]]; then
|
|
371
|
+
IFS=$'\n' detected_deps=($(printf '%s\n' "${detected_deps[@]}" | sort -u)); unset IFS
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
if [[ ${#detected_deps[@]} -eq 0 ]]; then
|
|
375
|
+
CHECK_STATUS="SKIP"
|
|
376
|
+
CHECK_DETAIL="No external dependencies detected"
|
|
377
|
+
return
|
|
378
|
+
fi
|
|
379
|
+
|
|
380
|
+
local results=()
|
|
381
|
+
local worst="PASS"
|
|
382
|
+
|
|
383
|
+
for dep in "${detected_deps[@]}"; do
|
|
384
|
+
local dep_url="${DEP_URLS[$dep]:-}"
|
|
385
|
+
if [[ -z "$dep_url" ]]; then continue; fi
|
|
386
|
+
|
|
387
|
+
local dep_status
|
|
388
|
+
dep_status=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 10 "$dep_url" 2>/dev/null || echo "000")
|
|
389
|
+
|
|
390
|
+
if [[ "$dep_status" =~ ^(200|401|403)$ ]]; then
|
|
391
|
+
results+=("${dep} ✓")
|
|
392
|
+
else
|
|
393
|
+
results+=("${dep} ✗ (${dep_status})")
|
|
394
|
+
if [[ "$worst" != "FAIL" ]]; then worst="WARN"; fi
|
|
395
|
+
fi
|
|
396
|
+
done
|
|
397
|
+
|
|
398
|
+
CHECK_STATUS="$worst"
|
|
399
|
+
CHECK_DETAIL=$(IFS=", "; echo "${results[*]}")
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
# ─── Check 8: SSL Certificate ────────────────────────────────────────
|
|
403
|
+
check_ssl() {
|
|
404
|
+
# Get certificate expiry
|
|
405
|
+
local cert_output
|
|
406
|
+
cert_output=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null)
|
|
407
|
+
|
|
408
|
+
if [[ -z "$cert_output" ]]; then
|
|
409
|
+
CHECK_STATUS="FAIL"
|
|
410
|
+
CHECK_DETAIL="Could not connect to $DOMAIN:443 for SSL check"
|
|
411
|
+
return
|
|
412
|
+
fi
|
|
413
|
+
|
|
414
|
+
local expiry_date
|
|
415
|
+
expiry_date=$(echo "$cert_output" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
|
|
416
|
+
|
|
417
|
+
if [[ -z "$expiry_date" ]]; then
|
|
418
|
+
CHECK_STATUS="FAIL"
|
|
419
|
+
CHECK_DETAIL="Could not parse SSL certificate"
|
|
420
|
+
return
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
# Calculate days until expiry
|
|
424
|
+
local expiry_epoch now_epoch days_left
|
|
425
|
+
expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null || date --date="$expiry_date" +%s 2>/dev/null)
|
|
426
|
+
now_epoch=$(date +%s)
|
|
427
|
+
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
|
|
428
|
+
|
|
429
|
+
# Verify cert matches domain
|
|
430
|
+
local cert_cn
|
|
431
|
+
cert_cn=$(echo "$cert_output" | openssl x509 -noout -subject 2>/dev/null | grep -oP 'CN\s*=\s*\K.*' || echo "")
|
|
432
|
+
local cert_san
|
|
433
|
+
cert_san=$(echo "$cert_output" | openssl x509 -noout -ext subjectAltName 2>/dev/null || echo "")
|
|
434
|
+
|
|
435
|
+
local domain_match=false
|
|
436
|
+
if echo "$cert_cn $cert_san" | grep -qi "$DOMAIN"; then
|
|
437
|
+
domain_match=true
|
|
438
|
+
fi
|
|
439
|
+
# Wildcard check
|
|
440
|
+
local parent_domain
|
|
441
|
+
parent_domain=$(echo "$DOMAIN" | sed 's/^[^.]*\.//')
|
|
442
|
+
if echo "$cert_cn $cert_san" | grep -qi "\*\.$parent_domain"; then
|
|
443
|
+
domain_match=true
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
if ((days_left < 0)); then
|
|
447
|
+
CHECK_STATUS="FAIL"
|
|
448
|
+
CHECK_DETAIL="Certificate EXPIRED ${days_left#-} days ago!"
|
|
449
|
+
elif ((days_left < SSL_FAIL_DAYS)); then
|
|
450
|
+
CHECK_STATUS="FAIL"
|
|
451
|
+
CHECK_DETAIL="Certificate expires in ${days_left} days (< ${SSL_FAIL_DAYS}d threshold)"
|
|
452
|
+
elif ((days_left < SSL_WARN_DAYS)); then
|
|
453
|
+
CHECK_STATUS="WARN"
|
|
454
|
+
CHECK_DETAIL="Valid, expires in ${days_left} days (renew soon)"
|
|
455
|
+
elif ! $domain_match; then
|
|
456
|
+
CHECK_STATUS="WARN"
|
|
457
|
+
CHECK_DETAIL="Valid (${days_left}d) but domain mismatch — CN: ${cert_cn}"
|
|
458
|
+
else
|
|
459
|
+
CHECK_STATUS="PASS"
|
|
460
|
+
CHECK_DETAIL="Valid, expires in ${days_left} days"
|
|
461
|
+
fi
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
465
|
+
# Main execution
|
|
466
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
467
|
+
|
|
468
|
+
TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
|
469
|
+
|
|
470
|
+
echo ""
|
|
471
|
+
echo -e "${C_BOLD}══════════════════════════════════════════════════════${C_RESET}"
|
|
472
|
+
echo -e " ${C_BOLD}DEPLOY VERIFICATION${C_RESET} — ${PROJECT_NAME:-unknown}"
|
|
473
|
+
echo -e " URL: ${URL}"
|
|
474
|
+
echo -e " Timestamp: ${TIMESTAMP}"
|
|
475
|
+
if [[ -n "${SUPABASE_REF:-}" ]]; then
|
|
476
|
+
echo -e " Supabase: ${SUPABASE_REF}"
|
|
477
|
+
fi
|
|
478
|
+
echo -e "${C_BOLD}══════════════════════════════════════════════════════${C_RESET}"
|
|
479
|
+
echo ""
|
|
480
|
+
|
|
481
|
+
# Run checks — HTTP first (critical gate)
|
|
482
|
+
with_retry "HTTP Status" check_http
|
|
483
|
+
|
|
484
|
+
if $CRITICAL_FAIL; then
|
|
485
|
+
echo -e "\n ${C_RED}${C_BOLD}CRITICAL: Site is unreachable — skipping remaining checks${C_RESET}"
|
|
486
|
+
echo -e " ${C_RED}Action: vercel rollback --prod${C_RESET}\n"
|
|
487
|
+
else
|
|
488
|
+
with_retry "Auth Flow" check_auth
|
|
489
|
+
with_retry "Console Errors" check_console
|
|
490
|
+
with_retry "API Latency" check_latency
|
|
491
|
+
with_retry "DB Connectivity" check_db
|
|
492
|
+
with_retry "Core Web Vitals" check_cwv
|
|
493
|
+
with_retry "Dependencies" check_deps
|
|
494
|
+
with_retry "SSL Certificate" check_ssl
|
|
495
|
+
fi
|
|
496
|
+
|
|
497
|
+
# ─── Print results ────────────────────────────────────────────────────
|
|
498
|
+
echo ""
|
|
499
|
+
for line in "${RESULTS[@]}"; do
|
|
500
|
+
echo -e "$line"
|
|
501
|
+
done
|
|
502
|
+
|
|
503
|
+
END_TIME=$(date +%s%N)
|
|
504
|
+
ELAPSED_MS=$(( (END_TIME - START_TIME) / 1000000 ))
|
|
505
|
+
ELAPSED_S=$(echo "scale=1; $ELAPSED_MS / 1000" | bc 2>/dev/null || echo "$((ELAPSED_MS / 1000))")
|
|
506
|
+
|
|
507
|
+
echo ""
|
|
508
|
+
echo -e "${C_BOLD}──────────────────────────────────────────────────────${C_RESET}"
|
|
509
|
+
echo -e " RESULT: ${C_GREEN}${PASS_COUNT} PASS${C_RESET} · ${C_YELLOW}${WARN_COUNT} WARN${C_RESET} · ${C_RED}${FAIL_COUNT} FAIL${C_RESET} · ${C_DIM}${SKIP_COUNT} SKIP${C_RESET}"
|
|
510
|
+
echo -e " Total verification time: ${ELAPSED_S}s"
|
|
511
|
+
echo -e "${C_BOLD}──────────────────────────────────────────────────────${C_RESET}"
|
|
512
|
+
|
|
513
|
+
# ─── Failure guidance ─────────────────────────────────────────────────
|
|
514
|
+
if ((FAIL_COUNT >= 2)) || $CRITICAL_FAIL; then
|
|
515
|
+
echo ""
|
|
516
|
+
echo -e " ${C_RED}${C_BOLD}⚠ ROLLBACK RECOMMENDED${C_RESET}"
|
|
517
|
+
echo -e " ${C_DIM}Run: vercel rollback --prod${C_RESET}"
|
|
518
|
+
echo -e " ${C_DIM}Then: bash $SCRIPT_DIR/verify.sh $URL${C_RESET}"
|
|
519
|
+
echo ""
|
|
520
|
+
elif ((FAIL_COUNT == 1)); then
|
|
521
|
+
echo ""
|
|
522
|
+
echo -e " ${C_YELLOW}${C_BOLD}⚠ 1 check failed — investigate before proceeding${C_RESET}"
|
|
523
|
+
echo -e " ${C_DIM}Check: vercel logs $URL${C_RESET}"
|
|
524
|
+
echo ""
|
|
525
|
+
fi
|
|
526
|
+
|
|
527
|
+
# ─── Schedule canary check at T+2min ─────────────────────────
|
|
528
|
+
# Only schedule if no critical/hard failures (site must be up for canary to make sense)
|
|
529
|
+
if ! $CRITICAL_FAIL && ((FAIL_COUNT == 0)); then
|
|
530
|
+
CANARY_SCRIPT="$SCRIPT_DIR/canary-check.sh"
|
|
531
|
+
if [[ -x "$CANARY_SCRIPT" ]]; then
|
|
532
|
+
echo ""
|
|
533
|
+
echo -e " ${C_DIM}Canary check scheduled in 120s (background)...${C_RESET}"
|
|
534
|
+
(
|
|
535
|
+
sleep 120
|
|
536
|
+
bash "$CANARY_SCRIPT" "$URL" --project-dir "$PROJECT_DIR" 2>&1 | tee -a "/tmp/.canary-output-$(date +%s).log"
|
|
537
|
+
) &
|
|
538
|
+
disown
|
|
539
|
+
fi
|
|
540
|
+
fi
|
|
541
|
+
|
|
542
|
+
# Exit code: 0 if no failures, 1 if any fail, 2 if critical
|
|
543
|
+
if $CRITICAL_FAIL; then
|
|
544
|
+
exit 2
|
|
545
|
+
elif ((FAIL_COUNT > 0)); then
|
|
546
|
+
exit 1
|
|
547
|
+
fi
|
|
548
|
+
exit 0
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-quieter
|
|
3
|
+
description: Tone down overly bold or visually aggressive designs. Reduces intensity while maintaining design quality and impact.
|
|
4
|
+
user-invokable: true
|
|
5
|
+
args:
|
|
6
|
+
- name: target
|
|
7
|
+
description: The feature or component to make quieter (optional)
|
|
8
|
+
required: false
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Reduce visual intensity in designs that are too bold, aggressive, or overstimulating, creating a more refined and approachable aesthetic without losing effectiveness.
|
|
12
|
+
|
|
13
|
+
## MANDATORY PREPARATION
|
|
14
|
+
|
|
15
|
+
### Read DESIGN.md (Authoritative Source)
|
|
16
|
+
|
|
17
|
+
Before any context gathering, check for the project's design decisions file:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cat .planning/DESIGN.md 2>/dev/null
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**If DESIGN.md exists:** This is the authoritative source for all design decisions in this project. Use its palette, typography, spacing, component, and motion decisions as your foundation. Do NOT override these decisions unless the user explicitly asks. Reference specific DESIGN.md decisions in your output (e.g., "Per DESIGN.md, the primary color is...").
|
|
24
|
+
|
|
25
|
+
**If DESIGN.md does not exist:** Proceed with normal context gathering below. Consider suggesting the user run `/qualia:discuss-phase` to establish design decisions.
|
|
26
|
+
|
|
27
|
+
### Context Gathering (Do This First)
|
|
28
|
+
|
|
29
|
+
You cannot do a great job without having necessary context, such as target audience (critical), desired use-cases (critical), brand personality/tone, and everything else that a great human designer would need as well.
|
|
30
|
+
|
|
31
|
+
Attempt to gather these from the current thread or codebase.
|
|
32
|
+
|
|
33
|
+
1. If you don't find *exact* information and have to infer from existing design and functionality, you MUST STOP and STOP and call the AskUserQuestionTool to clarify. whether you got it right.
|
|
34
|
+
2. Otherwise, if you can't fully infer or your level of confidence is medium or lower, you MUST STOP and call the AskUserQuestionTool to clarify. clarifying questions first to complete your context.
|
|
35
|
+
|
|
36
|
+
Do NOT proceed until you have answers. Guessing leads to generic design.
|
|
37
|
+
|
|
38
|
+
### Use frontend-master skill
|
|
39
|
+
|
|
40
|
+
Use the frontend-master skill for design principles and anti-patterns. Do NOT proceed until it has executed and you know all DO's and DON'Ts.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Assess Current State
|
|
45
|
+
|
|
46
|
+
Analyze what makes the design feel too intense:
|
|
47
|
+
|
|
48
|
+
1. **Identify intensity sources**:
|
|
49
|
+
- **Color saturation**: Overly bright or saturated colors
|
|
50
|
+
- **Contrast extremes**: Too much high-contrast juxtaposition
|
|
51
|
+
- **Visual weight**: Too many bold, heavy elements competing
|
|
52
|
+
- **Animation excess**: Too much motion or overly dramatic effects
|
|
53
|
+
- **Complexity**: Too many visual elements, patterns, or decorations
|
|
54
|
+
- **Scale**: Everything is large and loud with no hierarchy
|
|
55
|
+
|
|
56
|
+
2. **Understand the context**:
|
|
57
|
+
- What's the purpose? (Marketing vs tool vs reading experience)
|
|
58
|
+
- Who's the audience? (Some contexts need energy)
|
|
59
|
+
- What's working? (Don't throw away good ideas)
|
|
60
|
+
- What's the core message? (Preserve what matters)
|
|
61
|
+
|
|
62
|
+
If any of these are unclear from the codebase, STOP and call the AskUserQuestionTool to clarify.
|
|
63
|
+
|
|
64
|
+
**CRITICAL**: "Quieter" doesn't mean boring or generic. It means refined, sophisticated, and easier on the eyes. Think luxury, not laziness.
|
|
65
|
+
|
|
66
|
+
## Plan Refinement
|
|
67
|
+
|
|
68
|
+
Create a strategy to reduce intensity while maintaining impact:
|
|
69
|
+
|
|
70
|
+
- **Color approach**: Desaturate or shift to more sophisticated tones?
|
|
71
|
+
- **Hierarchy approach**: Which elements should stay bold (very few), which should recede?
|
|
72
|
+
- **Simplification approach**: What can be removed entirely?
|
|
73
|
+
- **Sophistication approach**: How can we signal quality through restraint?
|
|
74
|
+
|
|
75
|
+
**IMPORTANT**: Great quiet design is harder than great bold design. Subtlety requires precision.
|
|
76
|
+
|
|
77
|
+
## Refine the Design
|
|
78
|
+
|
|
79
|
+
Systematically reduce intensity across these dimensions:
|
|
80
|
+
|
|
81
|
+
### Color Refinement
|
|
82
|
+
- **Reduce saturation**: Shift from fully saturated to 70-85% saturation
|
|
83
|
+
- **Soften palette**: Replace bright colors with muted, sophisticated tones
|
|
84
|
+
- **Reduce color variety**: Use fewer colors more thoughtfully
|
|
85
|
+
- **Neutral dominance**: Let neutrals do more work, use color as accent (10% rule)
|
|
86
|
+
- **Gentler contrasts**: High contrast only where it matters most
|
|
87
|
+
- **Tinted grays**: Use warm or cool tinted grays instead of pure gray—adds sophistication without loudness
|
|
88
|
+
- **Never gray on color**: If you have gray text on a colored background, use a darker shade of that color or transparency instead
|
|
89
|
+
|
|
90
|
+
### Visual Weight Reduction
|
|
91
|
+
- **Typography**: Reduce font weights (900 → 600, 700 → 500), decrease sizes where appropriate
|
|
92
|
+
- **Hierarchy through subtlety**: Use weight, size, and space instead of color and boldness
|
|
93
|
+
- **White space**: Increase breathing room, reduce density
|
|
94
|
+
- **Borders & lines**: Reduce thickness, decrease opacity, or remove entirely
|
|
95
|
+
|
|
96
|
+
### Simplification
|
|
97
|
+
- **Remove decorative elements**: Gradients, shadows, patterns, textures that don't serve purpose
|
|
98
|
+
- **Simplify shapes**: Reduce border radius extremes, simplify custom shapes
|
|
99
|
+
- **Reduce layering**: Flatten visual hierarchy where possible
|
|
100
|
+
- **Clean up effects**: Reduce or remove blur effects, glows, multiple shadows
|
|
101
|
+
|
|
102
|
+
### Motion Reduction
|
|
103
|
+
- **Reduce animation intensity**: Shorter distances (10-20px instead of 40px), gentler easing
|
|
104
|
+
- **Remove decorative animations**: Keep functional motion, remove flourishes
|
|
105
|
+
- **Subtle micro-interactions**: Replace dramatic effects with gentle feedback
|
|
106
|
+
- **Refined easing**: Use ease-out-quart for smooth, understated motion—never bounce or elastic
|
|
107
|
+
- **Remove animations entirely** if they're not serving a clear purpose
|
|
108
|
+
|
|
109
|
+
### Composition Refinement
|
|
110
|
+
- **Reduce scale jumps**: Smaller contrast between sizes creates calmer feeling
|
|
111
|
+
- **Align to grid**: Bring rogue elements back into systematic alignment
|
|
112
|
+
- **Even out spacing**: Replace extreme spacing variations with consistent rhythm
|
|
113
|
+
|
|
114
|
+
**NEVER**:
|
|
115
|
+
- Make everything the same size/weight (hierarchy still matters)
|
|
116
|
+
- Remove all color (quiet ≠ grayscale)
|
|
117
|
+
- Eliminate all personality (maintain character through refinement)
|
|
118
|
+
- Sacrifice usability for aesthetics (functional elements still need clear affordances)
|
|
119
|
+
- Make everything small and light (some anchors needed)
|
|
120
|
+
|
|
121
|
+
## Verify Quality
|
|
122
|
+
|
|
123
|
+
Ensure refinement maintains quality:
|
|
124
|
+
|
|
125
|
+
- **Still functional**: Can users still accomplish tasks easily?
|
|
126
|
+
- **Still distinctive**: Does it have character, or is it generic now?
|
|
127
|
+
- **Better reading**: Is text easier to read for extended periods?
|
|
128
|
+
- **Sophistication**: Does it feel more refined and premium?
|
|
129
|
+
|
|
130
|
+
Remember: Quiet design is confident design. It doesn't need to shout. Less is more, but less is also harder. Refine with precision and maintain intentionality.
|