prizmkit 1.1.57 → 1.1.60
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/bin/create-prizmkit.js +8 -6
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/agent-adapter.js +38 -0
- package/bundled/adapters/codex/paths.js +27 -0
- package/bundled/adapters/codex/rules-adapter.js +30 -0
- package/bundled/adapters/codex/settings-adapter.js +27 -0
- package/bundled/adapters/codex/skill-adapter.js +65 -0
- package/bundled/adapters/codex/team-adapter.js +37 -0
- package/bundled/dev-pipeline/.env.example +2 -1
- package/bundled/dev-pipeline/README.md +10 -7
- package/bundled/dev-pipeline/lib/common.sh +278 -37
- package/bundled/dev-pipeline/run-bugfix.sh +10 -61
- package/bundled/dev-pipeline/run-feature.sh +10 -78
- package/bundled/dev-pipeline/run-recovery.sh +10 -46
- package/bundled/dev-pipeline/run-refactor.sh +10 -61
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/utils.py +6 -4
- package/bundled/dev-pipeline-windows/.env.example +28 -0
- package/bundled/dev-pipeline-windows/README.md +30 -0
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
- package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +26 -18
- package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/feature-planner/SKILL.md +9 -2
- package/bundled/skills/prizmkit-init/SKILL.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
- package/bundled/skills-windows/app-planner/SKILL.md +639 -0
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
- package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
- package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
- package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
- package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
- package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +68 -0
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
- package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
- package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
- package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
- package/package.json +3 -2
- package/src/clean.js +73 -2
- package/src/config.js +159 -50
- package/src/detect-platform.js +16 -8
- package/src/external-skills.js +26 -19
- package/src/index.js +31 -9
- package/src/manifest.js +6 -2
- package/src/metadata.js +43 -5
- package/src/platforms.js +36 -0
- package/src/prompts.js +31 -6
- package/src/runtimes.js +20 -0
- package/src/scaffold.js +314 -110
- package/src/upgrade.js +81 -41
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
dev-pipeline environment preflight checker.
|
|
4
|
+
|
|
5
|
+
Detects database type from .prizmkit/plans/feature-list.json / .prizmkit/config.json,
|
|
6
|
+
verifies env vars, tests connectivity, and checks migration status.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 preflight-check.py [.prizmkit/plans/feature-list.json]
|
|
10
|
+
|
|
11
|
+
Output: PREFLIGHT lines to stdout (✓ / ⚠ / ℹ), JSON summary to stderr.
|
|
12
|
+
Exit code: 0 = all clear, 1 = warnings found, 2 = error.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import glob
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
# ── Config ──────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
ENV_FILES = [".env.local", ".env", ".env.development.local", ".env.development"]
|
|
25
|
+
|
|
26
|
+
# (group_label, regex_pattern) — matched against env var names.
|
|
27
|
+
# IMPORTANT: use non-capturing groups (?:...) inside patterns so that
|
|
28
|
+
# group(1) in the scan regex captures the full variable name.
|
|
29
|
+
DB_VAR_PATTERNS = [
|
|
30
|
+
("SUPABASE_URL", r"(?:NEXT_PUBLIC_)?SUPABASE_URL"),
|
|
31
|
+
("SUPABASE_KEY", r"(?:NEXT_PUBLIC_)?SUPABASE_(?:ANON_KEY|SERVICE_ROLE_KEY)"),
|
|
32
|
+
("DATABASE_URL", r"DATABASE_URL"),
|
|
33
|
+
("DB_CONNECTION", r"DB_(?:HOST|CONNECTION|URL|PORT|NAME|USER|PASSWORD)"),
|
|
34
|
+
("FIREBASE", r"(?:NEXT_PUBLIC_)?FIREBASE_(?:API_KEY|PROJECT_ID|AUTH_DOMAIN)"),
|
|
35
|
+
("MONGODB", r"MONGO(?:DB)?_(?:URI|URL|CONNECTION)"),
|
|
36
|
+
("REDIS", r"REDIS_(?:URL|HOST|PORT)"),
|
|
37
|
+
("MYSQL", r"(?:MYSQL|PLANETSCALE)_(?:URL|HOST|DATABASE)"),
|
|
38
|
+
("POSTGRES", r"(?:PG|POSTGRES(?:QL)?)_(?:URL|HOST|CONNECTION)"),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
DB_KEYWORDS = [
|
|
42
|
+
"migration", "database", "create table", "table", "rls",
|
|
43
|
+
"storage bucket", "schema", "model", "prisma", "drizzle",
|
|
44
|
+
"sequelize", "typeorm", "supabase", "firebase", "mongodb",
|
|
45
|
+
"postgres", "mysql", "sqlite", "redis",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
warnings = []
|
|
49
|
+
passes = []
|
|
50
|
+
infos = []
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def out(level, msg):
|
|
54
|
+
"""Print a preflight line and collect it."""
|
|
55
|
+
print(f"PREFLIGHT {level} {msg}")
|
|
56
|
+
if level == "⚠":
|
|
57
|
+
warnings.append(msg)
|
|
58
|
+
elif level == "✓":
|
|
59
|
+
passes.append(msg)
|
|
60
|
+
else:
|
|
61
|
+
infos.append(msg)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── 1. Detect DB type and DB-related features ──────────────────
|
|
65
|
+
|
|
66
|
+
def detect_db(feature_list_path):
|
|
67
|
+
"""Return (db_type_str, list_of_feature_ids_with_db)."""
|
|
68
|
+
db_str = ""
|
|
69
|
+
db_features = []
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
with open(feature_list_path) as f:
|
|
73
|
+
data = json.load(f)
|
|
74
|
+
except Exception:
|
|
75
|
+
return "", []
|
|
76
|
+
|
|
77
|
+
db_str = data.get("global_context", {}).get("database", "")
|
|
78
|
+
|
|
79
|
+
if not db_str:
|
|
80
|
+
try:
|
|
81
|
+
with open(".prizmkit/config.json") as f:
|
|
82
|
+
cfg = json.load(f)
|
|
83
|
+
db_str = cfg.get("tech_stack", {}).get("database", "")
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
if not db_str:
|
|
88
|
+
return "", []
|
|
89
|
+
|
|
90
|
+
for feat in data.get("features", []):
|
|
91
|
+
desc = (feat.get("description", "") + " " + feat.get("title", "")).lower()
|
|
92
|
+
if any(k in desc for k in DB_KEYWORDS):
|
|
93
|
+
db_features.append(feat["id"])
|
|
94
|
+
|
|
95
|
+
return db_str, db_features
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ── 2. Scan env files for DB connection vars ────────────────────
|
|
99
|
+
|
|
100
|
+
def scan_env_vars():
|
|
101
|
+
"""Return (env_file_used, {var_name: value})."""
|
|
102
|
+
for env_file in ENV_FILES:
|
|
103
|
+
if not os.path.isfile(env_file):
|
|
104
|
+
continue
|
|
105
|
+
found = {}
|
|
106
|
+
with open(env_file) as f:
|
|
107
|
+
content = f.read()
|
|
108
|
+
for _group, pattern in DB_VAR_PATTERNS:
|
|
109
|
+
for m in re.finditer(
|
|
110
|
+
r"^(?!#)("+pattern + r")\s*=\s*(.+)",
|
|
111
|
+
content,
|
|
112
|
+
re.MULTILINE | re.IGNORECASE,
|
|
113
|
+
):
|
|
114
|
+
var_name = m.group(1)
|
|
115
|
+
var_val = m.group(2).strip().strip('"').strip("'")
|
|
116
|
+
if var_val:
|
|
117
|
+
found[var_name] = var_val
|
|
118
|
+
if found:
|
|
119
|
+
return env_file, found
|
|
120
|
+
return None, {}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ── 3. Connectivity checks (per DB type) ───────────────────────
|
|
124
|
+
|
|
125
|
+
def _curl_code(url, headers=None, timeout=10):
|
|
126
|
+
"""Run curl and return HTTP status code string."""
|
|
127
|
+
cmd = ["curl", "-s", "-o", os.devnull, "-w", "%{http_code}",
|
|
128
|
+
"--max-time", str(timeout), url]
|
|
129
|
+
for h in (headers or []):
|
|
130
|
+
cmd += ["-H", h]
|
|
131
|
+
try:
|
|
132
|
+
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout + 5)
|
|
133
|
+
return r.stdout.strip()
|
|
134
|
+
except Exception:
|
|
135
|
+
return "000"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _get_var(env_vars, *patterns):
|
|
139
|
+
"""Find first env var matching any pattern."""
|
|
140
|
+
for pat in patterns:
|
|
141
|
+
for k, v in env_vars.items():
|
|
142
|
+
if re.match(pat + "$", k, re.IGNORECASE) and v:
|
|
143
|
+
return k, v
|
|
144
|
+
return None, None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def check_connectivity(db_type, env_vars):
|
|
148
|
+
"""Test database connectivity. Returns True if connected."""
|
|
149
|
+
dt = db_type.lower()
|
|
150
|
+
|
|
151
|
+
# ── Supabase ──
|
|
152
|
+
if "supabase" in dt:
|
|
153
|
+
_, url = _get_var(env_vars, r"(?:NEXT_PUBLIC_)?SUPABASE_URL")
|
|
154
|
+
_, key = _get_var(env_vars, r"(?:NEXT_PUBLIC_)?SUPABASE_ANON_KEY")
|
|
155
|
+
if not key:
|
|
156
|
+
_, key = _get_var(env_vars, r"SUPABASE_SERVICE_ROLE_KEY")
|
|
157
|
+
if not (url and key):
|
|
158
|
+
out("⚠", "Supabase URL or anon key not found — cannot test connectivity")
|
|
159
|
+
return False
|
|
160
|
+
# Find a table from first migration to test against
|
|
161
|
+
test_table = "profiles"
|
|
162
|
+
mig_files = sorted(glob.glob("supabase/migrations/*.sql"))
|
|
163
|
+
if mig_files:
|
|
164
|
+
with open(mig_files[0]) as f:
|
|
165
|
+
for line in f:
|
|
166
|
+
m = re.search(r"CREATE TABLE\s+(?:public\.)?(\w+)", line, re.I)
|
|
167
|
+
if m:
|
|
168
|
+
test_table = m.group(1)
|
|
169
|
+
break
|
|
170
|
+
code = _curl_code(
|
|
171
|
+
f"{url}/rest/v1/{test_table}?limit=0",
|
|
172
|
+
[f"apikey: {key}", f"Authorization: Bearer {key}"],
|
|
173
|
+
)
|
|
174
|
+
if code == "200":
|
|
175
|
+
out("✓", "Database API reachable (Supabase)")
|
|
176
|
+
return True
|
|
177
|
+
else:
|
|
178
|
+
out("⚠", f"Database API unreachable (Supabase HTTP {code})")
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
# ── PostgreSQL / Neon ──
|
|
182
|
+
if any(k in dt for k in ("postgres", "pg", "neon")):
|
|
183
|
+
_, db_url = _get_var(env_vars, r"DATABASE_URL", r"POSTGRES(QL)?_URL", r"PG_URL")
|
|
184
|
+
if not db_url:
|
|
185
|
+
out("⚠", "No DATABASE_URL found — cannot test PostgreSQL connectivity")
|
|
186
|
+
return False
|
|
187
|
+
try:
|
|
188
|
+
r = subprocess.run(
|
|
189
|
+
["pg_isready", "-d", db_url],
|
|
190
|
+
capture_output=True, text=True, timeout=10,
|
|
191
|
+
)
|
|
192
|
+
if r.returncode == 0:
|
|
193
|
+
out("✓", "PostgreSQL reachable")
|
|
194
|
+
return True
|
|
195
|
+
out("⚠", f"PostgreSQL unreachable: {r.stderr.strip()}")
|
|
196
|
+
return False
|
|
197
|
+
except FileNotFoundError:
|
|
198
|
+
out("ℹ", "pg_isready not installed — cannot verify PostgreSQL connectivity")
|
|
199
|
+
return False
|
|
200
|
+
except Exception as e:
|
|
201
|
+
out("⚠", f"PostgreSQL connectivity test failed: {e}")
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
# ── MySQL / PlanetScale / MariaDB ──
|
|
205
|
+
if any(k in dt for k in ("mysql", "planetscale", "mariadb")):
|
|
206
|
+
_, db_url = _get_var(env_vars, r"DATABASE_URL", r"MYSQL_(URL|HOST)")
|
|
207
|
+
if not db_url:
|
|
208
|
+
out("⚠", "No DATABASE_URL found — cannot test MySQL connectivity")
|
|
209
|
+
return False
|
|
210
|
+
try:
|
|
211
|
+
hostport = db_url.split("@")[-1].split("/")[0] if "@" in db_url else db_url
|
|
212
|
+
host = hostport.split(":")[0]
|
|
213
|
+
port_args = ["-P", hostport.split(":")[1]] if ":" in hostport else []
|
|
214
|
+
r = subprocess.run(
|
|
215
|
+
["mysqladmin", "ping", "-h", host] + port_args,
|
|
216
|
+
capture_output=True, text=True, timeout=10,
|
|
217
|
+
)
|
|
218
|
+
if "alive" in r.stdout.lower():
|
|
219
|
+
out("✓", "MySQL reachable")
|
|
220
|
+
return True
|
|
221
|
+
out("⚠", "MySQL unreachable")
|
|
222
|
+
return False
|
|
223
|
+
except FileNotFoundError:
|
|
224
|
+
out("ℹ", "mysqladmin not installed — cannot verify MySQL connectivity")
|
|
225
|
+
return False
|
|
226
|
+
except Exception as e:
|
|
227
|
+
out("⚠", f"MySQL connectivity test failed: {e}")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
# ── MongoDB ──
|
|
231
|
+
if "mongo" in dt:
|
|
232
|
+
_, db_url = _get_var(env_vars, r"MONGO(DB)?_(URI|URL|CONNECTION)", r"DATABASE_URL")
|
|
233
|
+
if not db_url:
|
|
234
|
+
out("⚠", "No MongoDB URI found — cannot test connectivity")
|
|
235
|
+
return False
|
|
236
|
+
try:
|
|
237
|
+
r = subprocess.run(
|
|
238
|
+
["mongosh", "--eval", "db.runCommand({ping:1})", db_url, "--quiet"],
|
|
239
|
+
capture_output=True, text=True, timeout=10,
|
|
240
|
+
)
|
|
241
|
+
if r.returncode == 0:
|
|
242
|
+
out("✓", "MongoDB reachable")
|
|
243
|
+
return True
|
|
244
|
+
out("⚠", "MongoDB unreachable")
|
|
245
|
+
return False
|
|
246
|
+
except FileNotFoundError:
|
|
247
|
+
out("ℹ", "mongosh not installed — cannot verify MongoDB connectivity")
|
|
248
|
+
return False
|
|
249
|
+
except Exception as e:
|
|
250
|
+
out("⚠", f"MongoDB connectivity test failed: {e}")
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
# ── Firebase ──
|
|
254
|
+
if "firebase" in dt:
|
|
255
|
+
_, project_id = _get_var(env_vars, r"(NEXT_PUBLIC_)?FIREBASE_PROJECT_ID")
|
|
256
|
+
if not project_id:
|
|
257
|
+
out("⚠", "No Firebase project ID found — cannot test connectivity")
|
|
258
|
+
return False
|
|
259
|
+
code = _curl_code(
|
|
260
|
+
f"https://firestore.googleapis.com/v1/projects/{project_id}/databases/(default)/documents?pageSize=0"
|
|
261
|
+
)
|
|
262
|
+
if code in ("200", "401", "403"):
|
|
263
|
+
out("✓", "Firebase project reachable")
|
|
264
|
+
return True
|
|
265
|
+
out("⚠", f"Firebase unreachable (HTTP {code})")
|
|
266
|
+
return False
|
|
267
|
+
|
|
268
|
+
# ── Generic DATABASE_URL fallback ──
|
|
269
|
+
_, db_url = _get_var(env_vars, r"DATABASE_URL")
|
|
270
|
+
if db_url and "://" in db_url:
|
|
271
|
+
proto = db_url.split("://")[0]
|
|
272
|
+
if proto in ("postgres", "postgresql"):
|
|
273
|
+
try:
|
|
274
|
+
r = subprocess.run(
|
|
275
|
+
["pg_isready", "-d", db_url],
|
|
276
|
+
capture_output=True, text=True, timeout=10,
|
|
277
|
+
)
|
|
278
|
+
ok = r.returncode == 0
|
|
279
|
+
out("✓" if ok else "⚠", f"PostgreSQL {'reachable' if ok else 'unreachable'}")
|
|
280
|
+
return ok
|
|
281
|
+
except FileNotFoundError:
|
|
282
|
+
out("ℹ", "pg_isready not installed — cannot verify connectivity")
|
|
283
|
+
elif proto in ("mysql", "mariadb"):
|
|
284
|
+
out("ℹ", "MySQL DATABASE_URL found — install mysqladmin to verify connectivity")
|
|
285
|
+
elif proto in ("mongodb", "mongodb+srv"):
|
|
286
|
+
out("ℹ", "MongoDB DATABASE_URL found — install mongosh to verify connectivity")
|
|
287
|
+
else:
|
|
288
|
+
out("ℹ", f"DATABASE_URL found (protocol: {proto}) — cannot auto-verify")
|
|
289
|
+
return False
|
|
290
|
+
|
|
291
|
+
out("ℹ", f"Database type \"{db_type}\" detected but no connection variables found")
|
|
292
|
+
return False
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# ── 4. Migration status ────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
def check_migrations(db_type, env_vars, connected):
|
|
298
|
+
"""Check whether migrations have been applied."""
|
|
299
|
+
dt = db_type.lower()
|
|
300
|
+
checked = False
|
|
301
|
+
|
|
302
|
+
# ── Prisma ──
|
|
303
|
+
if os.path.isdir("prisma/migrations") or os.path.isfile("prisma/schema.prisma"):
|
|
304
|
+
checked = True
|
|
305
|
+
try:
|
|
306
|
+
env = os.environ.copy()
|
|
307
|
+
env.update(env_vars)
|
|
308
|
+
r = subprocess.run(
|
|
309
|
+
["npx", "prisma", "migrate", "status"],
|
|
310
|
+
capture_output=True, text=True, timeout=30, env=env,
|
|
311
|
+
)
|
|
312
|
+
if "not yet been applied" in r.stdout.lower():
|
|
313
|
+
out("⚠", "Prisma: unapplied migrations detected")
|
|
314
|
+
for line in r.stdout.splitlines():
|
|
315
|
+
if "not yet been applied" in line.lower() or line.strip().startswith("- "):
|
|
316
|
+
out("⚠", f" {line.strip()}")
|
|
317
|
+
elif r.returncode == 0:
|
|
318
|
+
out("✓", "Prisma: all migrations applied")
|
|
319
|
+
else:
|
|
320
|
+
snippet = (r.stderr or r.stdout or "").strip()[:200]
|
|
321
|
+
snippet = re.sub(r"://[^\s]+@", "://[REDACTED]@", snippet)
|
|
322
|
+
out("⚠", f"Prisma migrate status failed: {snippet}")
|
|
323
|
+
except FileNotFoundError:
|
|
324
|
+
out("ℹ", "npx not found — cannot check Prisma migration status")
|
|
325
|
+
except Exception as e:
|
|
326
|
+
out("⚠", f"Prisma check failed: {e}")
|
|
327
|
+
|
|
328
|
+
# ── Drizzle ──
|
|
329
|
+
if os.path.isdir("drizzle") and glob.glob("drizzle/*.sql"):
|
|
330
|
+
checked = True
|
|
331
|
+
out("ℹ", "Drizzle migrations found — verify with `npx drizzle-kit push` or `npx drizzle-kit migrate`")
|
|
332
|
+
|
|
333
|
+
# ── Supabase raw SQL ──
|
|
334
|
+
if os.path.isdir("supabase/migrations") and "supabase" in dt:
|
|
335
|
+
checked = True
|
|
336
|
+
url = env_vars.get("NEXT_PUBLIC_SUPABASE_URL", env_vars.get("SUPABASE_URL", ""))
|
|
337
|
+
key = env_vars.get("NEXT_PUBLIC_SUPABASE_ANON_KEY", env_vars.get("SUPABASE_ANON_KEY", ""))
|
|
338
|
+
|
|
339
|
+
if url and key and connected:
|
|
340
|
+
for mig in sorted(glob.glob("supabase/migrations/*.sql")):
|
|
341
|
+
with open(mig) as f:
|
|
342
|
+
content = f.read()
|
|
343
|
+
bn = os.path.basename(mig)
|
|
344
|
+
for match in re.finditer(
|
|
345
|
+
r"CREATE TABLE\s+(?:(public|auth|storage)\.)?(\w+)",
|
|
346
|
+
content, re.I,
|
|
347
|
+
):
|
|
348
|
+
schema = (match.group(1) or "public").lower()
|
|
349
|
+
tbl = match.group(2)
|
|
350
|
+
if schema != "public":
|
|
351
|
+
continue # auth/storage tables not accessible via REST API
|
|
352
|
+
code = _curl_code(
|
|
353
|
+
f"{url}/rest/v1/{tbl}?limit=0",
|
|
354
|
+
[f"apikey: {key}", f"Authorization: Bearer {key}"],
|
|
355
|
+
timeout=5,
|
|
356
|
+
)
|
|
357
|
+
if code == "200":
|
|
358
|
+
out("✓", f"{bn}: table '{tbl}' exists")
|
|
359
|
+
else:
|
|
360
|
+
out("⚠", f"{bn}: table '{tbl}' NOT FOUND — migration may not be applied")
|
|
361
|
+
else:
|
|
362
|
+
n = len(glob.glob("supabase/migrations/*.sql"))
|
|
363
|
+
out("ℹ", f"{n} Supabase migration file(s) found — cannot verify without API connection")
|
|
364
|
+
|
|
365
|
+
# ── Knex / Rails / generic ──
|
|
366
|
+
for mig_dir in ("migrations", "db/migrate", "db/migrations"):
|
|
367
|
+
if os.path.isdir(mig_dir) and not checked:
|
|
368
|
+
checked = True
|
|
369
|
+
n = len(os.listdir(mig_dir))
|
|
370
|
+
out("ℹ", f"{n} migration file(s) in {mig_dir}/ — verify manually that all are applied")
|
|
371
|
+
|
|
372
|
+
if not checked:
|
|
373
|
+
out("ℹ", "No migration directory detected — skipping migration check")
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# ── 5. Dev server ──────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
def check_dev_server(feature_list_path):
|
|
379
|
+
"""Check if dev server is running (from browser_interaction URLs)."""
|
|
380
|
+
try:
|
|
381
|
+
with open(feature_list_path) as f:
|
|
382
|
+
data = json.load(f)
|
|
383
|
+
except Exception:
|
|
384
|
+
return
|
|
385
|
+
checked_bases = set()
|
|
386
|
+
for feat in data.get("features", []):
|
|
387
|
+
bi = feat.get("browser_interaction")
|
|
388
|
+
if bi and isinstance(bi, dict) and bi.get("url"):
|
|
389
|
+
m = re.match(r"(https?://[^/]+)", bi["url"])
|
|
390
|
+
if m:
|
|
391
|
+
base = m.group(1)
|
|
392
|
+
if base in checked_bases:
|
|
393
|
+
continue
|
|
394
|
+
checked_bases.add(base)
|
|
395
|
+
code = _curl_code(base, timeout=5)
|
|
396
|
+
if code in ("200", "302"):
|
|
397
|
+
out("✓", f"Dev server reachable at {base}")
|
|
398
|
+
else:
|
|
399
|
+
out("ℹ", f"Dev server not running at {base} (AI sessions can start it)")
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# ── Main ────────────────────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
def main():
|
|
405
|
+
feature_list = sys.argv[1] if len(sys.argv) > 1 else ".prizmkit/plans/feature-list.json"
|
|
406
|
+
|
|
407
|
+
if not os.path.isfile(feature_list):
|
|
408
|
+
print(f"PREFLIGHT ⚠ Feature list not found: {feature_list}")
|
|
409
|
+
sys.exit(2)
|
|
410
|
+
|
|
411
|
+
# 1. Detect database
|
|
412
|
+
db_type, db_features = detect_db(feature_list)
|
|
413
|
+
if not db_type:
|
|
414
|
+
print("PREFLIGHT ℹ No database configured in global_context — skipping DB checks")
|
|
415
|
+
check_dev_server(feature_list)
|
|
416
|
+
_print_summary()
|
|
417
|
+
return
|
|
418
|
+
if not db_features:
|
|
419
|
+
print(f"PREFLIGHT ℹ Database: {db_type} (no features reference DB — skipping detailed checks)")
|
|
420
|
+
check_dev_server(feature_list)
|
|
421
|
+
_print_summary()
|
|
422
|
+
return
|
|
423
|
+
|
|
424
|
+
print(f"PREFLIGHT ℹ Database: {db_type}")
|
|
425
|
+
print(f"PREFLIGHT ℹ DB-related features: {', '.join(db_features)}")
|
|
426
|
+
|
|
427
|
+
# 2. Env vars
|
|
428
|
+
env_file, env_vars = scan_env_vars()
|
|
429
|
+
if not env_file:
|
|
430
|
+
out("⚠", "No env file found (.env.local, .env, etc.) — database connection will likely fail")
|
|
431
|
+
elif not env_vars:
|
|
432
|
+
out("⚠", f"{env_file} exists but no database connection variables detected")
|
|
433
|
+
else:
|
|
434
|
+
for var_name in sorted(env_vars.keys()):
|
|
435
|
+
out("✓", f"{var_name} configured")
|
|
436
|
+
|
|
437
|
+
# 3. Connectivity
|
|
438
|
+
connected = check_connectivity(db_type, env_vars)
|
|
439
|
+
|
|
440
|
+
# 4. Migrations
|
|
441
|
+
check_migrations(db_type, env_vars, connected)
|
|
442
|
+
|
|
443
|
+
# 5. Dev server
|
|
444
|
+
check_dev_server(feature_list)
|
|
445
|
+
|
|
446
|
+
_print_summary()
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def _print_summary():
|
|
450
|
+
"""Print JSON summary to stderr and set exit code."""
|
|
451
|
+
summary = {
|
|
452
|
+
"pass_count": len(passes),
|
|
453
|
+
"warn_count": len(warnings),
|
|
454
|
+
"info_count": len(infos),
|
|
455
|
+
"warnings": warnings,
|
|
456
|
+
}
|
|
457
|
+
print(json.dumps(summary), file=sys.stderr)
|
|
458
|
+
sys.exit(1 if warnings else 0)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
if __name__ == "__main__":
|
|
462
|
+
main()
|