rebar-mcp 2.0.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/.claude/agents/template-writer.md +43 -0
- package/.claude/agents/test-runner.md +47 -0
- package/.claude/mcp.json +9 -0
- package/.claude/settings.json +29 -0
- package/.claude/skills/ /SKILL.md +21 -0
- package/.claude/skills/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SKILL.md +21 -0
- package/.claude/skills/bmmibwetxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwjgvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwsesxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibwxufxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmibx3r9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmji0lrkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjiniphxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjio86zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjiolfbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjit1lvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bmmjita1qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibweu3/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwjh4/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwsey/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibwxup/SKILL.md +21 -0
- package/.claude/skills/bnd-mmibx3rg/SKILL.md +21 -0
- package/.claude/skills/bnd-mmji0lrp/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjinipm/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjio875/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjiolfg/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjit1m3/SKILL.md +21 -0
- package/.claude/skills/bnd-mmjita1x/SKILL.md +21 -0
- package/.claude/skills/coercion-test/SKILL.md +50 -0
- package/.claude/skills/large-skill/SKILL.md +21 -0
- package/.claude/skills/long-desc-skill/SKILL.md +21 -0
- package/.claude/skills/mcp-dev/SKILL.md +61 -0
- package/.claude/skills/nl-mmibweus/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwjhf/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwsf7/SKILL.md +25 -0
- package/.claude/skills/nl-mmibwxvq/SKILL.md +25 -0
- package/.claude/skills/nl-mmibx3rt/SKILL.md +25 -0
- package/.claude/skills/nl-mmji0lrz/SKILL.md +25 -0
- package/.claude/skills/nl-mmjinipx/SKILL.md +25 -0
- package/.claude/skills/nl-mmjio87f/SKILL.md +25 -0
- package/.claude/skills/nl-mmjiolfs/SKILL.md +25 -0
- package/.claude/skills/nl-mmjit1mc/SKILL.md +25 -0
- package/.claude/skills/nl-mmjita26/SKILL.md +25 -0
- package/.claude/skills/rapid-1/SKILL.md +21 -0
- package/.claude/skills/rapid-2/SKILL.md +21 -0
- package/.claude/skills/rapid-3/SKILL.md +21 -0
- package/.claude/skills/rapid-4/SKILL.md +21 -0
- package/.claude/skills/rapid-5/SKILL.md +21 -0
- package/.claude/skills/test/", /"malicious/": /"true/SKILL.md" +69 -0
- package/.claude/skills/test-emoji-/360/237/230/200-skill/SKILL.md +69 -0
- package/.claude/skills/test-skill/SKILL.md +69 -0
- package/.claude/skills/test; rm -rf /; skill/SKILL.md +69 -0
- package/.claude/skills/test<script>alert(1)</script>skill/SKILL.md +69 -0
- package/.claudeignore +5 -0
- package/.mcp.json +3 -0
- package/CHANGELOG.md +29 -0
- package/CLAUDE.md +76 -0
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/ROADMAP.md +526 -0
- package/ccboot-PRD-v1.0.docx.md +732 -0
- package/ccboot-v1.2.0-enforcement-spec.md +1272 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +674 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +25 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +118 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +62 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +15 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/scaffolding.d.ts +277 -0
- package/dist/schemas/scaffolding.d.ts.map +1 -0
- package/dist/schemas/scaffolding.js +133 -0
- package/dist/schemas/scaffolding.js.map +1 -0
- package/dist/services/claudemd-generator.d.ts +16 -0
- package/dist/services/claudemd-generator.d.ts.map +1 -0
- package/dist/services/claudemd-generator.js +426 -0
- package/dist/services/claudemd-generator.js.map +1 -0
- package/dist/services/codex-generator.d.ts +6 -0
- package/dist/services/codex-generator.d.ts.map +1 -0
- package/dist/services/codex-generator.js +35 -0
- package/dist/services/codex-generator.js.map +1 -0
- package/dist/services/cursor-generator.d.ts +15 -0
- package/dist/services/cursor-generator.d.ts.map +1 -0
- package/dist/services/cursor-generator.js +134 -0
- package/dist/services/cursor-generator.js.map +1 -0
- package/dist/services/file-ops.d.ts +48 -0
- package/dist/services/file-ops.d.ts.map +1 -0
- package/dist/services/file-ops.js +153 -0
- package/dist/services/file-ops.js.map +1 -0
- package/dist/services/output-formatter.d.ts +57 -0
- package/dist/services/output-formatter.d.ts.map +1 -0
- package/dist/services/output-formatter.js +88 -0
- package/dist/services/output-formatter.js.map +1 -0
- package/dist/services/platform-detect.d.ts +14 -0
- package/dist/services/platform-detect.d.ts.map +1 -0
- package/dist/services/platform-detect.js +63 -0
- package/dist/services/platform-detect.js.map +1 -0
- package/dist/services/project-analyzer.d.ts +71 -0
- package/dist/services/project-analyzer.d.ts.map +1 -0
- package/dist/services/project-analyzer.js +595 -0
- package/dist/services/project-analyzer.js.map +1 -0
- package/dist/services/rules-engine.d.ts +41 -0
- package/dist/services/rules-engine.d.ts.map +1 -0
- package/dist/services/rules-engine.js +304 -0
- package/dist/services/rules-engine.js.map +1 -0
- package/dist/services/strictness.d.ts +37 -0
- package/dist/services/strictness.d.ts.map +1 -0
- package/dist/services/strictness.js +182 -0
- package/dist/services/strictness.js.map +1 -0
- package/dist/services/template-engine.d.ts +16 -0
- package/dist/services/template-engine.d.ts.map +1 -0
- package/dist/services/template-engine.js +85 -0
- package/dist/services/template-engine.js.map +1 -0
- package/dist/services/validation.d.ts +41 -0
- package/dist/services/validation.d.ts.map +1 -0
- package/dist/services/validation.js +104 -0
- package/dist/services/validation.js.map +1 -0
- package/dist/services/windsurf-generator.d.ts +15 -0
- package/dist/services/windsurf-generator.d.ts.map +1 -0
- package/dist/services/windsurf-generator.js +127 -0
- package/dist/services/windsurf-generator.js.map +1 -0
- package/dist/tests/enforcement.test.d.ts +2 -0
- package/dist/tests/enforcement.test.d.ts.map +1 -0
- package/dist/tests/enforcement.test.js +541 -0
- package/dist/tests/enforcement.test.js.map +1 -0
- package/dist/tests/enterprise.test.d.ts +2 -0
- package/dist/tests/enterprise.test.d.ts.map +1 -0
- package/dist/tests/enterprise.test.js +353 -0
- package/dist/tests/enterprise.test.js.map +1 -0
- package/dist/tests/fuzzing.test.d.ts +2 -0
- package/dist/tests/fuzzing.test.d.ts.map +1 -0
- package/dist/tests/fuzzing.test.js +596 -0
- package/dist/tests/fuzzing.test.js.map +1 -0
- package/dist/tests/knowledge.test.d.ts +2 -0
- package/dist/tests/knowledge.test.d.ts.map +1 -0
- package/dist/tests/knowledge.test.js +292 -0
- package/dist/tests/knowledge.test.js.map +1 -0
- package/dist/tests/management.test.d.ts +2 -0
- package/dist/tests/management.test.d.ts.map +1 -0
- package/dist/tests/management.test.js +338 -0
- package/dist/tests/management.test.js.map +1 -0
- package/dist/tests/scaffolding.test.d.ts +2 -0
- package/dist/tests/scaffolding.test.d.ts.map +1 -0
- package/dist/tests/scaffolding.test.js +419 -0
- package/dist/tests/scaffolding.test.js.map +1 -0
- package/dist/tests/test-utils.d.ts +76 -0
- package/dist/tests/test-utils.d.ts.map +1 -0
- package/dist/tests/test-utils.js +171 -0
- package/dist/tests/test-utils.js.map +1 -0
- package/dist/tests/tool-harness.d.ts +18 -0
- package/dist/tests/tool-harness.d.ts.map +1 -0
- package/dist/tests/tool-harness.js +51 -0
- package/dist/tests/tool-harness.js.map +1 -0
- package/dist/tools/enterprise.d.ts +8 -0
- package/dist/tools/enterprise.d.ts.map +1 -0
- package/dist/tools/enterprise.js +571 -0
- package/dist/tools/enterprise.js.map +1 -0
- package/dist/tools/knowledge.d.ts +7 -0
- package/dist/tools/knowledge.d.ts.map +1 -0
- package/dist/tools/knowledge.js +120 -0
- package/dist/tools/knowledge.js.map +1 -0
- package/dist/tools/management.d.ts +10 -0
- package/dist/tools/management.d.ts.map +1 -0
- package/dist/tools/management.js +1541 -0
- package/dist/tools/management.js.map +1 -0
- package/dist/tools/scaffolding.d.ts +8 -0
- package/dist/tools/scaffolding.d.ts.map +1 -0
- package/dist/tools/scaffolding.js +736 -0
- package/dist/tools/scaffolding.js.map +1 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/landing/app/layout.tsx +30 -0
- package/landing/app/page.tsx +944 -0
- package/landing/next-env.d.ts +6 -0
- package/landing/next.config.js +6 -0
- package/landing/package-lock.json +896 -0
- package/landing/package.json +20 -0
- package/landing/tsconfig.json +40 -0
- package/package.json +49 -0
- package/rebar-v2.0.0-platform-spec.md +1567 -0
- package/server.json +20 -0
- package/src/cli.ts +735 -0
- package/src/constants.ts +131 -0
- package/src/index.ts +54 -0
- package/src/schemas/common.ts +22 -0
- package/src/schemas/scaffolding.ts +161 -0
- package/src/services/claudemd-generator.ts +481 -0
- package/src/services/codex-generator.ts +44 -0
- package/src/services/cursor-generator.ts +153 -0
- package/src/services/file-ops.ts +172 -0
- package/src/services/platform-detect.ts +80 -0
- package/src/services/project-analyzer.ts +690 -0
- package/src/services/rules-engine.ts +353 -0
- package/src/services/strictness.ts +202 -0
- package/src/services/template-engine.ts +119 -0
- package/src/services/validation.ts +138 -0
- package/src/services/windsurf-generator.ts +145 -0
- package/src/tests/enforcement.test.ts +794 -0
- package/src/tests/enterprise.test.ts +483 -0
- package/src/tests/fuzzing.test.ts +690 -0
- package/src/tests/knowledge.test.ts +371 -0
- package/src/tests/management.test.ts +451 -0
- package/src/tests/scaffolding.test.ts +575 -0
- package/src/tests/test-utils.ts +206 -0
- package/src/tests/tool-harness.ts +70 -0
- package/src/tools/enterprise.ts +666 -0
- package/src/tools/knowledge.ts +162 -0
- package/src/tools/management.ts +1706 -0
- package/src/tools/scaffolding.ts +909 -0
- package/src/types.ts +93 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/templates/agents/explore.md +41 -0
- package/templates/agents/plan.md +73 -0
- package/templates/agents/security-auditor.md +77 -0
- package/templates/agents/test-runner.md +60 -0
- package/templates/claudemd/fastapi.md +49 -0
- package/templates/claudemd/monorepo.md +48 -0
- package/templates/claudemd/nextjs.md +52 -0
- package/templates/claudemd/react-spa.md +50 -0
- package/templates/claudemd/springboot.md +50 -0
- package/templates/hooks/danger-blocker.json +11 -0
- package/templates/hooks/format-on-write.json +17 -0
- package/templates/hooks/lint-on-write.json +16 -0
- package/templates/hooks/secret-detector.json +11 -0
- package/templates/skills/code-review.md +68 -0
- package/templates/skills/documentation.md +62 -0
- package/templates/skills/performance-audit.md +80 -0
- package/templates/skills/security-scan.md +66 -0
- package/templates/skills/test-writer.md +56 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, FormEvent } from "react";
|
|
4
|
+
|
|
5
|
+
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
6
|
+
const SUPABASE_ANON_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
7
|
+
|
|
8
|
+
// Simple Supabase fetch helper (no SDK needed)
|
|
9
|
+
async function supabaseInsert(table: string, data: Record<string, unknown>) {
|
|
10
|
+
const response = await fetch(`${SUPABASE_URL}/rest/v1/${table}`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
'apikey': SUPABASE_ANON_KEY,
|
|
15
|
+
'Authorization': `Bearer ${SUPABASE_ANON_KEY}`,
|
|
16
|
+
'Prefer': 'return=minimal'
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify(data)
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
const error = await response.json().catch(() => ({}));
|
|
23
|
+
throw new Error(error.message || 'Failed to submit');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface LiveStats {
|
|
30
|
+
npmDownloads: number | null;
|
|
31
|
+
githubStars: number | null;
|
|
32
|
+
githubForks: number | null;
|
|
33
|
+
openIssues: number | null;
|
|
34
|
+
loading: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function Home() {
|
|
38
|
+
const [stats, setStats] = useState<LiveStats>({
|
|
39
|
+
npmDownloads: null,
|
|
40
|
+
githubStars: null,
|
|
41
|
+
githubForks: null,
|
|
42
|
+
openIssues: null,
|
|
43
|
+
loading: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
async function fetchStats() {
|
|
48
|
+
try {
|
|
49
|
+
// Fetch npm downloads (last week)
|
|
50
|
+
const npmRes = await fetch(
|
|
51
|
+
"https://api.npmjs.org/downloads/point/last-week/rebar-mcp"
|
|
52
|
+
);
|
|
53
|
+
const npmData = npmRes.ok ? await npmRes.json() : null;
|
|
54
|
+
|
|
55
|
+
// Fetch GitHub stats
|
|
56
|
+
const ghRes = await fetch(
|
|
57
|
+
"https://api.github.com/repos/RCOLKITT/rebar-mcp"
|
|
58
|
+
);
|
|
59
|
+
const ghData = ghRes.ok ? await ghRes.json() : null;
|
|
60
|
+
|
|
61
|
+
setStats({
|
|
62
|
+
npmDownloads: npmData?.downloads ?? null,
|
|
63
|
+
githubStars: ghData?.stargazers_count ?? null,
|
|
64
|
+
githubForks: ghData?.forks_count ?? null,
|
|
65
|
+
openIssues: ghData?.open_issues_count ?? null,
|
|
66
|
+
loading: false,
|
|
67
|
+
});
|
|
68
|
+
} catch {
|
|
69
|
+
setStats((prev) => ({ ...prev, loading: false }));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fetchStats();
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div style={{
|
|
78
|
+
minHeight: '100vh',
|
|
79
|
+
background: 'linear-gradient(180deg, #0a0a0a 0%, #141414 50%, #0a0a0a 100%)',
|
|
80
|
+
color: '#fff',
|
|
81
|
+
padding: '40px 20px'
|
|
82
|
+
}}>
|
|
83
|
+
<div style={{ maxWidth: '1000px', margin: '0 auto' }}>
|
|
84
|
+
{/* Hero */}
|
|
85
|
+
<div style={{ textAlign: 'center', marginBottom: '80px', paddingTop: '40px' }}>
|
|
86
|
+
{/* Logo */}
|
|
87
|
+
<div style={{ marginBottom: '32px' }}>
|
|
88
|
+
<RebarLogo />
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div style={{
|
|
92
|
+
display: 'inline-block',
|
|
93
|
+
background: 'rgba(245, 158, 11, 0.1)',
|
|
94
|
+
border: '1px solid rgba(245, 158, 11, 0.3)',
|
|
95
|
+
borderRadius: '20px',
|
|
96
|
+
padding: '6px 16px',
|
|
97
|
+
fontSize: '0.85rem',
|
|
98
|
+
color: '#f59e0b',
|
|
99
|
+
marginBottom: '24px',
|
|
100
|
+
fontWeight: 500
|
|
101
|
+
}}>
|
|
102
|
+
Works with Claude Code, Cursor, Windsurf & Codex
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<h1 style={{
|
|
106
|
+
fontSize: '3.8rem',
|
|
107
|
+
fontWeight: 800,
|
|
108
|
+
marginBottom: '24px',
|
|
109
|
+
lineHeight: '1.05',
|
|
110
|
+
letterSpacing: '-0.03em'
|
|
111
|
+
}}>
|
|
112
|
+
<span style={{ color: '#fff' }}>Reinforcement for</span><br/>
|
|
113
|
+
<span style={{
|
|
114
|
+
background: 'linear-gradient(90deg, #f59e0b 0%, #ef4444 100%)',
|
|
115
|
+
WebkitBackgroundClip: 'text',
|
|
116
|
+
WebkitTextFillColor: 'transparent'
|
|
117
|
+
}}>AI-generated code</span>
|
|
118
|
+
</h1>
|
|
119
|
+
|
|
120
|
+
<p style={{
|
|
121
|
+
fontSize: '1.35rem',
|
|
122
|
+
color: '#a1a1aa',
|
|
123
|
+
maxWidth: '650px',
|
|
124
|
+
margin: '0 auto 40px',
|
|
125
|
+
lineHeight: '1.6'
|
|
126
|
+
}}>
|
|
127
|
+
Enforcement hooks, quality audits, and opinionated templates that prevent
|
|
128
|
+
AI coding tools from shipping broken code.
|
|
129
|
+
</p>
|
|
130
|
+
|
|
131
|
+
<div style={{
|
|
132
|
+
background: '#18181b',
|
|
133
|
+
borderRadius: '12px',
|
|
134
|
+
padding: '18px 28px',
|
|
135
|
+
fontFamily: '"JetBrains Mono", monospace',
|
|
136
|
+
fontSize: '1rem',
|
|
137
|
+
border: '1px solid #27272a',
|
|
138
|
+
display: 'inline-block',
|
|
139
|
+
marginBottom: '16px'
|
|
140
|
+
}}>
|
|
141
|
+
<span style={{ color: '#71717a' }}>$</span>{' '}
|
|
142
|
+
<span style={{ color: '#f59e0b' }}>claude mcp add</span> rebar --{' '}
|
|
143
|
+
<span style={{ color: '#22c55e' }}>npx rebar-mcp</span>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<p style={{ color: '#52525b', fontSize: '0.9rem' }}>
|
|
147
|
+
One command. Works immediately.
|
|
148
|
+
</p>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* Live Stats Bar */}
|
|
152
|
+
<div style={{
|
|
153
|
+
display: 'flex',
|
|
154
|
+
justifyContent: 'center',
|
|
155
|
+
gap: '48px',
|
|
156
|
+
marginBottom: '80px',
|
|
157
|
+
padding: '24px 40px',
|
|
158
|
+
background: 'rgba(255,255,255,0.02)',
|
|
159
|
+
borderRadius: '16px',
|
|
160
|
+
border: '1px solid #27272a',
|
|
161
|
+
flexWrap: 'wrap'
|
|
162
|
+
}}>
|
|
163
|
+
<LiveStat
|
|
164
|
+
label="npm downloads"
|
|
165
|
+
value={stats.npmDownloads}
|
|
166
|
+
suffix="/week"
|
|
167
|
+
loading={stats.loading}
|
|
168
|
+
/>
|
|
169
|
+
<LiveStat
|
|
170
|
+
label="GitHub stars"
|
|
171
|
+
value={stats.githubStars}
|
|
172
|
+
loading={stats.loading}
|
|
173
|
+
/>
|
|
174
|
+
<LiveStat
|
|
175
|
+
label="Forks"
|
|
176
|
+
value={stats.githubForks}
|
|
177
|
+
loading={stats.loading}
|
|
178
|
+
/>
|
|
179
|
+
<LiveStat
|
|
180
|
+
label="Open issues"
|
|
181
|
+
value={stats.openIssues}
|
|
182
|
+
loading={stats.loading}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{/* Problem/Solution */}
|
|
187
|
+
<div style={{
|
|
188
|
+
display: 'grid',
|
|
189
|
+
gridTemplateColumns: '1fr 1fr',
|
|
190
|
+
gap: '24px',
|
|
191
|
+
marginBottom: '80px'
|
|
192
|
+
}}>
|
|
193
|
+
<div style={{
|
|
194
|
+
background: 'rgba(239, 68, 68, 0.05)',
|
|
195
|
+
border: '1px solid rgba(239, 68, 68, 0.2)',
|
|
196
|
+
borderRadius: '16px',
|
|
197
|
+
padding: '30px'
|
|
198
|
+
}}>
|
|
199
|
+
<h3 style={{ color: '#ef4444', marginTop: 0, fontSize: '1.1rem', fontWeight: 600 }}>
|
|
200
|
+
Without Rebar
|
|
201
|
+
</h3>
|
|
202
|
+
<ul style={{ color: '#a1a1aa', lineHeight: '2.2', paddingLeft: '20px', margin: 0 }}>
|
|
203
|
+
<li>"I've completed the implementation" <span style={{ color: '#71717a' }}>(no tests run)</span></li>
|
|
204
|
+
<li>Empty catch blocks swallow errors silently</li>
|
|
205
|
+
<li><code style={{ color: '#ef4444', background: 'rgba(239,68,68,0.1)', padding: '2px 6px', borderRadius: '4px' }}>any</code> types everywhere — TypeScript theater</li>
|
|
206
|
+
<li>TODO comments shipped to production</li>
|
|
207
|
+
</ul>
|
|
208
|
+
</div>
|
|
209
|
+
<div style={{
|
|
210
|
+
background: 'rgba(34, 197, 94, 0.05)',
|
|
211
|
+
border: '1px solid rgba(34, 197, 94, 0.2)',
|
|
212
|
+
borderRadius: '16px',
|
|
213
|
+
padding: '30px'
|
|
214
|
+
}}>
|
|
215
|
+
<h3 style={{ color: '#22c55e', marginTop: 0, fontSize: '1.1rem', fontWeight: 600 }}>
|
|
216
|
+
With Rebar
|
|
217
|
+
</h3>
|
|
218
|
+
<ul style={{ color: '#a1a1aa', lineHeight: '2.2', paddingLeft: '20px', margin: 0 }}>
|
|
219
|
+
<li>Tests run after every file change</li>
|
|
220
|
+
<li>Lint errors block until fixed</li>
|
|
221
|
+
<li>Secrets detected and blocked pre-commit</li>
|
|
222
|
+
<li>Destructive commands require approval</li>
|
|
223
|
+
</ul>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
{/* Platforms */}
|
|
228
|
+
<div style={{
|
|
229
|
+
textAlign: 'center',
|
|
230
|
+
marginBottom: '80px',
|
|
231
|
+
padding: '50px 40px',
|
|
232
|
+
background: 'linear-gradient(135deg, rgba(245, 158, 11, 0.05) 0%, rgba(239, 68, 68, 0.05) 100%)',
|
|
233
|
+
borderRadius: '20px',
|
|
234
|
+
border: '1px solid rgba(245, 158, 11, 0.15)'
|
|
235
|
+
}}>
|
|
236
|
+
<h2 style={{ marginTop: 0, fontSize: '1.8rem', marginBottom: '12px', fontWeight: 700 }}>
|
|
237
|
+
One tool. Every AI coding platform.
|
|
238
|
+
</h2>
|
|
239
|
+
<p style={{ color: '#71717a', marginBottom: '32px' }}>
|
|
240
|
+
Rebar generates native configuration for each platform
|
|
241
|
+
</p>
|
|
242
|
+
<div style={{
|
|
243
|
+
display: 'grid',
|
|
244
|
+
gridTemplateColumns: 'repeat(4, 1fr)',
|
|
245
|
+
gap: '24px'
|
|
246
|
+
}}>
|
|
247
|
+
<Platform name="Claude Code" files="CLAUDE.md, hooks, skills" />
|
|
248
|
+
<Platform name="Cursor" files=".cursor/rules/" />
|
|
249
|
+
<Platform name="Windsurf" files=".windsurf/rules/" />
|
|
250
|
+
<Platform name="Codex CLI" files="AGENTS.md" />
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
{/* Strictness Profiles */}
|
|
255
|
+
<div style={{ marginBottom: '80px' }}>
|
|
256
|
+
<h2 style={{ textAlign: 'center', marginBottom: '16px', fontSize: '2rem', fontWeight: 700 }}>
|
|
257
|
+
Three strictness levels
|
|
258
|
+
</h2>
|
|
259
|
+
<p style={{ textAlign: 'center', color: '#71717a', marginBottom: '40px' }}>
|
|
260
|
+
From gentle nudges to iron-fisted enforcement
|
|
261
|
+
</p>
|
|
262
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}>
|
|
263
|
+
<ProfileCard
|
|
264
|
+
name="Standard"
|
|
265
|
+
emoji="⚡"
|
|
266
|
+
color="#3b82f6"
|
|
267
|
+
description="Notify on issues. Danger + secrets still block."
|
|
268
|
+
features={['Test results shown', 'Lint output displayed', 'Secrets blocked', 'Danger blocked']}
|
|
269
|
+
/>
|
|
270
|
+
<ProfileCard
|
|
271
|
+
name="Strict"
|
|
272
|
+
emoji="🛡️"
|
|
273
|
+
color="#f59e0b"
|
|
274
|
+
description="Block on test and lint failures."
|
|
275
|
+
features={['Tests must pass', 'Lint must pass', 'Secrets blocked', 'Danger blocked']}
|
|
276
|
+
recommended
|
|
277
|
+
/>
|
|
278
|
+
<ProfileCard
|
|
279
|
+
name="Paranoid"
|
|
280
|
+
emoji="🔒"
|
|
281
|
+
color="#ef4444"
|
|
282
|
+
description="Maximum enforcement. No shortcuts."
|
|
283
|
+
features={['All Strict rules', 'Files < 400 lines', 'No any types', 'No TODOs in commits']}
|
|
284
|
+
/>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{/* Quality Checks */}
|
|
289
|
+
<div style={{
|
|
290
|
+
background: 'rgba(255,255,255,0.02)',
|
|
291
|
+
borderRadius: '20px',
|
|
292
|
+
padding: '50px 40px',
|
|
293
|
+
marginBottom: '80px'
|
|
294
|
+
}}>
|
|
295
|
+
<h2 style={{ marginTop: 0, textAlign: 'center', marginBottom: '12px', fontSize: '2rem', fontWeight: 700 }}>
|
|
296
|
+
8 Quality Checks, Always On
|
|
297
|
+
</h2>
|
|
298
|
+
<p style={{ textAlign: 'center', color: '#71717a', marginBottom: '36px' }}>
|
|
299
|
+
Audit your code with <code style={{ background: '#27272a', padding: '2px 8px', borderRadius: '4px' }}>rebar audit</code>
|
|
300
|
+
</p>
|
|
301
|
+
<div style={{
|
|
302
|
+
display: 'grid',
|
|
303
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
304
|
+
gap: '16px'
|
|
305
|
+
}}>
|
|
306
|
+
<Check title="Test Coverage" desc="Tests must pass before task completion" />
|
|
307
|
+
<Check title="Error Handling" desc="No empty catch blocks or swallowed errors" />
|
|
308
|
+
<Check title="Type Safety" desc="No any types in TypeScript code" />
|
|
309
|
+
<Check title="Clean Code" desc="No TODO/FIXME in production code" />
|
|
310
|
+
<Check title="File Size" desc="Files under 400 lines, functions under 50" />
|
|
311
|
+
<Check title="Secret Detection" desc="API keys and tokens blocked from commits" />
|
|
312
|
+
<Check title="Dangerous Commands" desc="rm -rf, force push require approval" />
|
|
313
|
+
<Check title="Custom Rules" desc="Add your own rules in .rebar/rules.yaml" />
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
{/* CLI */}
|
|
318
|
+
<div style={{ marginBottom: '80px' }}>
|
|
319
|
+
<h2 style={{ textAlign: 'center', marginBottom: '16px', fontSize: '2rem', fontWeight: 700 }}>
|
|
320
|
+
Works in CI/CD too
|
|
321
|
+
</h2>
|
|
322
|
+
<p style={{ textAlign: 'center', color: '#71717a', marginBottom: '32px' }}>
|
|
323
|
+
Gate pull requests on quality score
|
|
324
|
+
</p>
|
|
325
|
+
<div style={{
|
|
326
|
+
background: '#18181b',
|
|
327
|
+
borderRadius: '12px',
|
|
328
|
+
padding: '24px',
|
|
329
|
+
fontFamily: '"JetBrains Mono", monospace',
|
|
330
|
+
fontSize: '0.9rem',
|
|
331
|
+
border: '1px solid #27272a',
|
|
332
|
+
overflow: 'auto'
|
|
333
|
+
}}>
|
|
334
|
+
<pre style={{ margin: 0, color: '#a1a1aa' }}>
|
|
335
|
+
{`# .github/workflows/rebar.yml
|
|
336
|
+
name: Rebar Code Quality
|
|
337
|
+
on: [pull_request]
|
|
338
|
+
|
|
339
|
+
jobs:
|
|
340
|
+
quality:
|
|
341
|
+
runs-on: ubuntu-latest
|
|
342
|
+
steps:
|
|
343
|
+
- uses: actions/checkout@v4
|
|
344
|
+
- run: npx rebar-mcp audit --threshold 70`}
|
|
345
|
+
</pre>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
|
|
349
|
+
{/* Pricing */}
|
|
350
|
+
<div style={{ marginBottom: '80px' }}>
|
|
351
|
+
<h2 style={{ textAlign: 'center', marginBottom: '16px', fontSize: '2rem', fontWeight: 700 }}>
|
|
352
|
+
Pricing
|
|
353
|
+
</h2>
|
|
354
|
+
<p style={{ textAlign: 'center', color: '#71717a', marginBottom: '40px' }}>
|
|
355
|
+
Open source core. Enterprise features coming soon.
|
|
356
|
+
</p>
|
|
357
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}>
|
|
358
|
+
<PricingCard
|
|
359
|
+
name="Free"
|
|
360
|
+
price="$0"
|
|
361
|
+
period="forever"
|
|
362
|
+
description="Everything you need to enforce AI code quality"
|
|
363
|
+
features={[
|
|
364
|
+
'15 MCP tools',
|
|
365
|
+
'4 compliance packs (HIPAA, SOC2, PCI-DSS, SOX)',
|
|
366
|
+
'5 CLAUDE.md templates',
|
|
367
|
+
'18 lifecycle hook events',
|
|
368
|
+
'Multi-platform support',
|
|
369
|
+
'JSON & dry-run modes',
|
|
370
|
+
'Community support'
|
|
371
|
+
]}
|
|
372
|
+
cta="Get Started"
|
|
373
|
+
ctaHref="https://github.com/RCOLKITT/rebar-mcp"
|
|
374
|
+
available
|
|
375
|
+
/>
|
|
376
|
+
<PricingCard
|
|
377
|
+
name="Pro"
|
|
378
|
+
price="TBD"
|
|
379
|
+
period=""
|
|
380
|
+
description="Advanced compliance and priority support"
|
|
381
|
+
features={[
|
|
382
|
+
'Everything in Free',
|
|
383
|
+
'ISO 42001, FedRAMP, GDPR packs',
|
|
384
|
+
'Priority GitHub support',
|
|
385
|
+
'Template customization',
|
|
386
|
+
'Early access to new features'
|
|
387
|
+
]}
|
|
388
|
+
cta="Join Waitlist"
|
|
389
|
+
ctaHref="#waitlist"
|
|
390
|
+
comingSoon
|
|
391
|
+
/>
|
|
392
|
+
<PricingCard
|
|
393
|
+
name="Enterprise"
|
|
394
|
+
price="Custom"
|
|
395
|
+
period=""
|
|
396
|
+
description="Team management and audit capabilities"
|
|
397
|
+
features={[
|
|
398
|
+
'Everything in Pro',
|
|
399
|
+
'Team policy dashboard',
|
|
400
|
+
'Centralized audit logs',
|
|
401
|
+
'Custom compliance packs',
|
|
402
|
+
'SSO/SAML integration',
|
|
403
|
+
'Dedicated support + SLA'
|
|
404
|
+
]}
|
|
405
|
+
cta="Contact Sales"
|
|
406
|
+
ctaHref="mailto:ryan@vasperacapital.com?subject=Rebar%20Enterprise"
|
|
407
|
+
comingSoon
|
|
408
|
+
/>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
{/* Compliance Badges */}
|
|
413
|
+
<div style={{
|
|
414
|
+
textAlign: 'center',
|
|
415
|
+
marginBottom: '80px',
|
|
416
|
+
padding: '40px',
|
|
417
|
+
background: 'rgba(255,255,255,0.02)',
|
|
418
|
+
borderRadius: '20px',
|
|
419
|
+
border: '1px solid #27272a'
|
|
420
|
+
}}>
|
|
421
|
+
<h3 style={{ marginTop: 0, marginBottom: '8px', fontSize: '1.3rem', fontWeight: 600 }}>
|
|
422
|
+
Compliance Packs Available Now
|
|
423
|
+
</h3>
|
|
424
|
+
<p style={{ color: '#71717a', marginBottom: '28px', fontSize: '0.9rem' }}>
|
|
425
|
+
Generate hooks and policies for major compliance frameworks
|
|
426
|
+
</p>
|
|
427
|
+
<div style={{ display: 'flex', justifyContent: 'center', gap: '32px', flexWrap: 'wrap' }}>
|
|
428
|
+
<ComplianceBadge name="HIPAA" />
|
|
429
|
+
<ComplianceBadge name="SOC 2" />
|
|
430
|
+
<ComplianceBadge name="PCI-DSS" />
|
|
431
|
+
<ComplianceBadge name="SOX" />
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
|
|
435
|
+
{/* Waitlist */}
|
|
436
|
+
<div id="waitlist" style={{
|
|
437
|
+
textAlign: 'center',
|
|
438
|
+
marginBottom: '80px',
|
|
439
|
+
padding: '50px 40px',
|
|
440
|
+
background: 'linear-gradient(135deg, rgba(245, 158, 11, 0.05) 0%, rgba(239, 68, 68, 0.05) 100%)',
|
|
441
|
+
borderRadius: '20px',
|
|
442
|
+
border: '1px solid rgba(245, 158, 11, 0.15)'
|
|
443
|
+
}}>
|
|
444
|
+
<h2 style={{ marginTop: 0, fontSize: '1.8rem', marginBottom: '12px', fontWeight: 700 }}>
|
|
445
|
+
Interested in Pro or Enterprise?
|
|
446
|
+
</h2>
|
|
447
|
+
<p style={{ color: '#a1a1aa', marginBottom: '28px', maxWidth: '500px', margin: '0 auto 28px' }}>
|
|
448
|
+
We're building team features and advanced compliance packs. Get notified when they're ready.
|
|
449
|
+
</p>
|
|
450
|
+
<WaitlistForm />
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
{/* CTA */}
|
|
454
|
+
<div style={{
|
|
455
|
+
textAlign: 'center',
|
|
456
|
+
marginBottom: '80px',
|
|
457
|
+
padding: '60px 40px',
|
|
458
|
+
background: 'linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(239, 68, 68, 0.08) 100%)',
|
|
459
|
+
borderRadius: '20px',
|
|
460
|
+
border: '1px solid rgba(245, 158, 11, 0.2)'
|
|
461
|
+
}}>
|
|
462
|
+
<h2 style={{ fontSize: '2.4rem', marginBottom: '16px', fontWeight: 700 }}>
|
|
463
|
+
Stop shipping broken AI code
|
|
464
|
+
</h2>
|
|
465
|
+
<p style={{ color: '#a1a1aa', marginBottom: '32px', fontSize: '1.1rem' }}>
|
|
466
|
+
One command to enforce quality across your entire team
|
|
467
|
+
</p>
|
|
468
|
+
<div style={{
|
|
469
|
+
background: '#18181b',
|
|
470
|
+
borderRadius: '12px',
|
|
471
|
+
padding: '20px 32px',
|
|
472
|
+
fontFamily: '"JetBrains Mono", monospace',
|
|
473
|
+
fontSize: '1.1rem',
|
|
474
|
+
border: '1px solid #27272a',
|
|
475
|
+
display: 'inline-block',
|
|
476
|
+
marginBottom: '24px'
|
|
477
|
+
}}>
|
|
478
|
+
npx rebar-mcp init --strictness strict
|
|
479
|
+
</div>
|
|
480
|
+
<p style={{ color: '#71717a' }}>
|
|
481
|
+
Or as MCP server: <code style={{ background: '#27272a', padding: '4px 10px', borderRadius: '6px' }}>claude mcp add rebar -- npx rebar-mcp</code>
|
|
482
|
+
</p>
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
{/* Footer */}
|
|
486
|
+
<div style={{
|
|
487
|
+
textAlign: 'center',
|
|
488
|
+
color: '#52525b',
|
|
489
|
+
paddingTop: '40px',
|
|
490
|
+
borderTop: '1px solid #27272a'
|
|
491
|
+
}}>
|
|
492
|
+
<p>
|
|
493
|
+
Built by{' '}
|
|
494
|
+
<a href="https://vasperacapital.com" style={{ color: '#f59e0b', textDecoration: 'none' }}>
|
|
495
|
+
Vaspera Capital
|
|
496
|
+
</a>
|
|
497
|
+
</p>
|
|
498
|
+
<p style={{ marginTop: '10px' }}>
|
|
499
|
+
<a href="https://github.com/RCOLKITT/rebar-mcp" style={{ color: '#71717a', textDecoration: 'none' }}>
|
|
500
|
+
GitHub
|
|
501
|
+
</a>
|
|
502
|
+
{' · '}
|
|
503
|
+
<a href="https://www.npmjs.com/package/rebar-mcp" style={{ color: '#71717a', textDecoration: 'none' }}>
|
|
504
|
+
npm
|
|
505
|
+
</a>
|
|
506
|
+
{' · '}
|
|
507
|
+
<a href="mailto:ryan@vasperacapital.com" style={{ color: '#71717a', textDecoration: 'none' }}>
|
|
508
|
+
Contact
|
|
509
|
+
</a>
|
|
510
|
+
</p>
|
|
511
|
+
</div>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function LiveStat({ label, value, suffix = "", loading }: { label: string; value: number | null; suffix?: string; loading: boolean }) {
|
|
518
|
+
return (
|
|
519
|
+
<div style={{ textAlign: 'center' }}>
|
|
520
|
+
<div style={{ fontSize: '2rem', fontWeight: 700, color: '#f59e0b', minHeight: '40px' }}>
|
|
521
|
+
{loading ? (
|
|
522
|
+
<span style={{ color: '#52525b' }}>—</span>
|
|
523
|
+
) : value !== null ? (
|
|
524
|
+
<>
|
|
525
|
+
{value.toLocaleString()}
|
|
526
|
+
{suffix && <span style={{ fontSize: '1rem', color: '#71717a' }}>{suffix}</span>}
|
|
527
|
+
</>
|
|
528
|
+
) : (
|
|
529
|
+
<span style={{ color: '#52525b' }}>—</span>
|
|
530
|
+
)}
|
|
531
|
+
</div>
|
|
532
|
+
<div style={{ color: '#71717a', fontSize: '0.85rem', marginTop: '4px' }}>{label}</div>
|
|
533
|
+
</div>
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function RebarLogo() {
|
|
538
|
+
return (
|
|
539
|
+
<svg width="200" height="60" viewBox="0 0 200 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
540
|
+
{/* Crossed rebar pattern */}
|
|
541
|
+
<defs>
|
|
542
|
+
<linearGradient id="rebarGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
543
|
+
<stop offset="0%" stopColor="#f59e0b" />
|
|
544
|
+
<stop offset="100%" stopColor="#ef4444" />
|
|
545
|
+
</linearGradient>
|
|
546
|
+
<linearGradient id="steelGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
547
|
+
<stop offset="0%" stopColor="#71717a" />
|
|
548
|
+
<stop offset="50%" stopColor="#a1a1aa" />
|
|
549
|
+
<stop offset="100%" stopColor="#71717a" />
|
|
550
|
+
</linearGradient>
|
|
551
|
+
</defs>
|
|
552
|
+
|
|
553
|
+
{/* Rebar icon - two crossed bars */}
|
|
554
|
+
<g transform="translate(10, 10)">
|
|
555
|
+
{/* Horizontal bar with ridges */}
|
|
556
|
+
<rect x="0" y="18" width="40" height="8" rx="2" fill="url(#rebarGradient)" />
|
|
557
|
+
<rect x="5" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
|
|
558
|
+
<rect x="12" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
|
|
559
|
+
<rect x="19" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
|
|
560
|
+
<rect x="26" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
|
|
561
|
+
<rect x="33" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
|
|
562
|
+
|
|
563
|
+
{/* Diagonal bar */}
|
|
564
|
+
<rect x="8" y="0" width="8" height="44" rx="2" fill="url(#steelGradient)" transform="rotate(30 20 22)" />
|
|
565
|
+
</g>
|
|
566
|
+
|
|
567
|
+
{/* Text */}
|
|
568
|
+
<text x="60" y="40" fontFamily="Inter, system-ui" fontSize="32" fontWeight="800" fill="white">
|
|
569
|
+
Rebar
|
|
570
|
+
</text>
|
|
571
|
+
</svg>
|
|
572
|
+
)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function Platform({ name, files }: { name: string; files: string }) {
|
|
576
|
+
return (
|
|
577
|
+
<div style={{
|
|
578
|
+
background: 'rgba(255,255,255,0.03)',
|
|
579
|
+
borderRadius: '12px',
|
|
580
|
+
padding: '20px 16px',
|
|
581
|
+
border: '1px solid rgba(255,255,255,0.05)'
|
|
582
|
+
}}>
|
|
583
|
+
<div style={{ fontWeight: 600, marginBottom: '8px', fontSize: '1.05rem' }}>{name}</div>
|
|
584
|
+
<div style={{ color: '#71717a', fontSize: '0.85rem', fontFamily: '"JetBrains Mono", monospace' }}>{files}</div>
|
|
585
|
+
</div>
|
|
586
|
+
)
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function ProfileCard({
|
|
590
|
+
name,
|
|
591
|
+
emoji,
|
|
592
|
+
color,
|
|
593
|
+
description,
|
|
594
|
+
features,
|
|
595
|
+
recommended
|
|
596
|
+
}: {
|
|
597
|
+
name: string;
|
|
598
|
+
emoji: string;
|
|
599
|
+
color: string;
|
|
600
|
+
description: string;
|
|
601
|
+
features: string[];
|
|
602
|
+
recommended?: boolean;
|
|
603
|
+
}) {
|
|
604
|
+
return (
|
|
605
|
+
<div style={{
|
|
606
|
+
background: `rgba(255,255,255,0.02)`,
|
|
607
|
+
borderRadius: '16px',
|
|
608
|
+
padding: '28px 24px',
|
|
609
|
+
border: recommended ? `2px solid ${color}` : '1px solid rgba(255,255,255,0.05)',
|
|
610
|
+
position: 'relative'
|
|
611
|
+
}}>
|
|
612
|
+
{recommended && (
|
|
613
|
+
<div style={{
|
|
614
|
+
position: 'absolute',
|
|
615
|
+
top: '-12px',
|
|
616
|
+
left: '50%',
|
|
617
|
+
transform: 'translateX(-50%)',
|
|
618
|
+
background: color,
|
|
619
|
+
color: '#000',
|
|
620
|
+
padding: '4px 12px',
|
|
621
|
+
borderRadius: '12px',
|
|
622
|
+
fontSize: '0.75rem',
|
|
623
|
+
fontWeight: 600
|
|
624
|
+
}}>
|
|
625
|
+
RECOMMENDED
|
|
626
|
+
</div>
|
|
627
|
+
)}
|
|
628
|
+
<div style={{ fontSize: '2rem', marginBottom: '12px' }}>{emoji}</div>
|
|
629
|
+
<h3 style={{ margin: '0 0 8px', fontSize: '1.3rem', color, fontWeight: 700 }}>{name}</h3>
|
|
630
|
+
<p style={{ color: '#a1a1aa', marginBottom: '20px', fontSize: '0.9rem', lineHeight: '1.5' }}>{description}</p>
|
|
631
|
+
<ul style={{ margin: 0, padding: 0, listStyle: 'none' }}>
|
|
632
|
+
{features.map((f, i) => (
|
|
633
|
+
<li key={i} style={{
|
|
634
|
+
color: '#71717a',
|
|
635
|
+
fontSize: '0.85rem',
|
|
636
|
+
marginBottom: '8px',
|
|
637
|
+
display: 'flex',
|
|
638
|
+
alignItems: 'center',
|
|
639
|
+
gap: '8px'
|
|
640
|
+
}}>
|
|
641
|
+
<span style={{ color }}>✓</span> {f}
|
|
642
|
+
</li>
|
|
643
|
+
))}
|
|
644
|
+
</ul>
|
|
645
|
+
</div>
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function Check({ title, desc }: { title: string; desc: string }) {
|
|
650
|
+
return (
|
|
651
|
+
<div style={{
|
|
652
|
+
display: 'flex',
|
|
653
|
+
alignItems: 'flex-start',
|
|
654
|
+
gap: '14px',
|
|
655
|
+
padding: '18px',
|
|
656
|
+
background: 'rgba(255,255,255,0.02)',
|
|
657
|
+
borderRadius: '12px',
|
|
658
|
+
border: '1px solid rgba(255,255,255,0.04)'
|
|
659
|
+
}}>
|
|
660
|
+
<span style={{
|
|
661
|
+
color: '#22c55e',
|
|
662
|
+
fontSize: '1.1rem',
|
|
663
|
+
background: 'rgba(34, 197, 94, 0.1)',
|
|
664
|
+
width: '28px',
|
|
665
|
+
height: '28px',
|
|
666
|
+
display: 'flex',
|
|
667
|
+
alignItems: 'center',
|
|
668
|
+
justifyContent: 'center',
|
|
669
|
+
borderRadius: '6px',
|
|
670
|
+
flexShrink: 0
|
|
671
|
+
}}>✓</span>
|
|
672
|
+
<div>
|
|
673
|
+
<div style={{ fontWeight: 600, marginBottom: '4px' }}>{title}</div>
|
|
674
|
+
<div style={{ color: '#71717a', fontSize: '0.9rem' }}>{desc}</div>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function PricingCard({
|
|
681
|
+
name,
|
|
682
|
+
price,
|
|
683
|
+
period,
|
|
684
|
+
description,
|
|
685
|
+
features,
|
|
686
|
+
cta,
|
|
687
|
+
ctaHref,
|
|
688
|
+
available,
|
|
689
|
+
comingSoon
|
|
690
|
+
}: {
|
|
691
|
+
name: string;
|
|
692
|
+
price: string;
|
|
693
|
+
period: string;
|
|
694
|
+
description: string;
|
|
695
|
+
features: string[];
|
|
696
|
+
cta: string;
|
|
697
|
+
ctaHref: string;
|
|
698
|
+
available?: boolean;
|
|
699
|
+
comingSoon?: boolean;
|
|
700
|
+
}) {
|
|
701
|
+
return (
|
|
702
|
+
<div style={{
|
|
703
|
+
background: available ? 'rgba(34, 197, 94, 0.03)' : 'rgba(255,255,255,0.02)',
|
|
704
|
+
borderRadius: '16px',
|
|
705
|
+
padding: '32px 28px',
|
|
706
|
+
border: available ? '2px solid rgba(34, 197, 94, 0.3)' : '1px solid rgba(255,255,255,0.05)',
|
|
707
|
+
position: 'relative',
|
|
708
|
+
display: 'flex',
|
|
709
|
+
flexDirection: 'column'
|
|
710
|
+
}}>
|
|
711
|
+
{available && (
|
|
712
|
+
<div style={{
|
|
713
|
+
position: 'absolute',
|
|
714
|
+
top: '-12px',
|
|
715
|
+
left: '50%',
|
|
716
|
+
transform: 'translateX(-50%)',
|
|
717
|
+
background: '#22c55e',
|
|
718
|
+
color: '#000',
|
|
719
|
+
padding: '4px 12px',
|
|
720
|
+
borderRadius: '12px',
|
|
721
|
+
fontSize: '0.75rem',
|
|
722
|
+
fontWeight: 600
|
|
723
|
+
}}>
|
|
724
|
+
AVAILABLE NOW
|
|
725
|
+
</div>
|
|
726
|
+
)}
|
|
727
|
+
{comingSoon && (
|
|
728
|
+
<div style={{
|
|
729
|
+
position: 'absolute',
|
|
730
|
+
top: '-12px',
|
|
731
|
+
left: '50%',
|
|
732
|
+
transform: 'translateX(-50%)',
|
|
733
|
+
background: '#52525b',
|
|
734
|
+
color: '#fff',
|
|
735
|
+
padding: '4px 12px',
|
|
736
|
+
borderRadius: '12px',
|
|
737
|
+
fontSize: '0.75rem',
|
|
738
|
+
fontWeight: 600
|
|
739
|
+
}}>
|
|
740
|
+
COMING SOON
|
|
741
|
+
</div>
|
|
742
|
+
)}
|
|
743
|
+
<h3 style={{ margin: '0 0 8px', fontSize: '1.4rem', fontWeight: 700 }}>{name}</h3>
|
|
744
|
+
<div style={{ marginBottom: '12px' }}>
|
|
745
|
+
<span style={{ fontSize: '2.5rem', fontWeight: 800, color: available ? '#22c55e' : '#fff' }}>{price}</span>
|
|
746
|
+
{period && <span style={{ color: '#71717a', fontSize: '0.9rem' }}> {period}</span>}
|
|
747
|
+
</div>
|
|
748
|
+
<p style={{ color: '#a1a1aa', marginBottom: '24px', fontSize: '0.9rem', lineHeight: '1.5' }}>{description}</p>
|
|
749
|
+
<ul style={{ margin: '0 0 28px', padding: 0, listStyle: 'none', flex: 1 }}>
|
|
750
|
+
{features.map((f, i) => (
|
|
751
|
+
<li key={i} style={{
|
|
752
|
+
color: '#a1a1aa',
|
|
753
|
+
fontSize: '0.85rem',
|
|
754
|
+
marginBottom: '10px',
|
|
755
|
+
display: 'flex',
|
|
756
|
+
alignItems: 'flex-start',
|
|
757
|
+
gap: '10px'
|
|
758
|
+
}}>
|
|
759
|
+
<span style={{ color: available ? '#22c55e' : '#71717a', flexShrink: 0 }}>✓</span>
|
|
760
|
+
<span>{f}</span>
|
|
761
|
+
</li>
|
|
762
|
+
))}
|
|
763
|
+
</ul>
|
|
764
|
+
<a
|
|
765
|
+
href={ctaHref}
|
|
766
|
+
style={{
|
|
767
|
+
display: 'block',
|
|
768
|
+
textAlign: 'center',
|
|
769
|
+
background: available ? '#22c55e' : comingSoon ? 'transparent' : '#f59e0b',
|
|
770
|
+
color: available ? '#000' : comingSoon ? '#a1a1aa' : '#000',
|
|
771
|
+
padding: '12px 20px',
|
|
772
|
+
borderRadius: '8px',
|
|
773
|
+
fontWeight: 600,
|
|
774
|
+
textDecoration: 'none',
|
|
775
|
+
border: comingSoon ? '1px solid #52525b' : 'none'
|
|
776
|
+
}}
|
|
777
|
+
>
|
|
778
|
+
{cta}
|
|
779
|
+
</a>
|
|
780
|
+
</div>
|
|
781
|
+
)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function ComplianceBadge({ name }: { name: string }) {
|
|
785
|
+
return (
|
|
786
|
+
<div style={{
|
|
787
|
+
background: 'rgba(245, 158, 11, 0.1)',
|
|
788
|
+
border: '1px solid rgba(245, 158, 11, 0.3)',
|
|
789
|
+
borderRadius: '8px',
|
|
790
|
+
padding: '12px 24px',
|
|
791
|
+
fontWeight: 600,
|
|
792
|
+
fontSize: '0.95rem',
|
|
793
|
+
color: '#f59e0b'
|
|
794
|
+
}}>
|
|
795
|
+
{name}
|
|
796
|
+
</div>
|
|
797
|
+
)
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function WaitlistForm() {
|
|
801
|
+
const [email, setEmail] = useState('');
|
|
802
|
+
const [interest, setInterest] = useState<'pro' | 'enterprise' | 'both'>('pro');
|
|
803
|
+
const [company, setCompany] = useState('');
|
|
804
|
+
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
805
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
806
|
+
|
|
807
|
+
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
|
808
|
+
e.preventDefault();
|
|
809
|
+
setStatus('loading');
|
|
810
|
+
setErrorMessage('');
|
|
811
|
+
|
|
812
|
+
try {
|
|
813
|
+
await supabaseInsert('waitlist', {
|
|
814
|
+
email,
|
|
815
|
+
interest,
|
|
816
|
+
company: company || null,
|
|
817
|
+
source: 'landing_page'
|
|
818
|
+
});
|
|
819
|
+
setStatus('success');
|
|
820
|
+
setEmail('');
|
|
821
|
+
setCompany('');
|
|
822
|
+
} catch (err) {
|
|
823
|
+
setStatus('error');
|
|
824
|
+
setErrorMessage(err instanceof Error ? err.message : 'Something went wrong');
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
if (status === 'success') {
|
|
829
|
+
return (
|
|
830
|
+
<div style={{
|
|
831
|
+
background: 'rgba(34, 197, 94, 0.1)',
|
|
832
|
+
border: '1px solid rgba(34, 197, 94, 0.3)',
|
|
833
|
+
borderRadius: '12px',
|
|
834
|
+
padding: '24px',
|
|
835
|
+
maxWidth: '400px',
|
|
836
|
+
margin: '0 auto'
|
|
837
|
+
}}>
|
|
838
|
+
<div style={{ color: '#22c55e', fontSize: '1.5rem', marginBottom: '8px' }}>✓</div>
|
|
839
|
+
<div style={{ color: '#22c55e', fontWeight: 600, marginBottom: '4px' }}>You're on the list!</div>
|
|
840
|
+
<div style={{ color: '#a1a1aa', fontSize: '0.9rem' }}>We'll notify you when Pro/Enterprise launches.</div>
|
|
841
|
+
</div>
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
return (
|
|
846
|
+
<form onSubmit={handleSubmit} style={{ maxWidth: '400px', margin: '0 auto' }}>
|
|
847
|
+
<div style={{ marginBottom: '16px' }}>
|
|
848
|
+
<input
|
|
849
|
+
type="email"
|
|
850
|
+
required
|
|
851
|
+
placeholder="you@company.com"
|
|
852
|
+
value={email}
|
|
853
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
854
|
+
style={{
|
|
855
|
+
width: '100%',
|
|
856
|
+
padding: '14px 18px',
|
|
857
|
+
borderRadius: '8px',
|
|
858
|
+
border: '1px solid #27272a',
|
|
859
|
+
background: '#18181b',
|
|
860
|
+
color: '#fff',
|
|
861
|
+
fontSize: '1rem',
|
|
862
|
+
outline: 'none'
|
|
863
|
+
}}
|
|
864
|
+
/>
|
|
865
|
+
</div>
|
|
866
|
+
|
|
867
|
+
<div style={{ marginBottom: '16px' }}>
|
|
868
|
+
<input
|
|
869
|
+
type="text"
|
|
870
|
+
placeholder="Company (optional)"
|
|
871
|
+
value={company}
|
|
872
|
+
onChange={(e) => setCompany(e.target.value)}
|
|
873
|
+
style={{
|
|
874
|
+
width: '100%',
|
|
875
|
+
padding: '14px 18px',
|
|
876
|
+
borderRadius: '8px',
|
|
877
|
+
border: '1px solid #27272a',
|
|
878
|
+
background: '#18181b',
|
|
879
|
+
color: '#fff',
|
|
880
|
+
fontSize: '1rem',
|
|
881
|
+
outline: 'none'
|
|
882
|
+
}}
|
|
883
|
+
/>
|
|
884
|
+
</div>
|
|
885
|
+
|
|
886
|
+
<div style={{ marginBottom: '20px', display: 'flex', gap: '12px', justifyContent: 'center' }}>
|
|
887
|
+
{(['pro', 'enterprise', 'both'] as const).map((opt) => (
|
|
888
|
+
<label
|
|
889
|
+
key={opt}
|
|
890
|
+
style={{
|
|
891
|
+
display: 'flex',
|
|
892
|
+
alignItems: 'center',
|
|
893
|
+
gap: '6px',
|
|
894
|
+
cursor: 'pointer',
|
|
895
|
+
color: interest === opt ? '#f59e0b' : '#71717a',
|
|
896
|
+
fontSize: '0.9rem'
|
|
897
|
+
}}
|
|
898
|
+
>
|
|
899
|
+
<input
|
|
900
|
+
type="radio"
|
|
901
|
+
name="interest"
|
|
902
|
+
value={opt}
|
|
903
|
+
checked={interest === opt}
|
|
904
|
+
onChange={() => setInterest(opt)}
|
|
905
|
+
style={{ accentColor: '#f59e0b' }}
|
|
906
|
+
/>
|
|
907
|
+
{opt.charAt(0).toUpperCase() + opt.slice(1)}
|
|
908
|
+
</label>
|
|
909
|
+
))}
|
|
910
|
+
</div>
|
|
911
|
+
|
|
912
|
+
{status === 'error' && (
|
|
913
|
+
<div style={{
|
|
914
|
+
color: '#ef4444',
|
|
915
|
+
fontSize: '0.85rem',
|
|
916
|
+
marginBottom: '12px',
|
|
917
|
+
padding: '8px 12px',
|
|
918
|
+
background: 'rgba(239, 68, 68, 0.1)',
|
|
919
|
+
borderRadius: '6px'
|
|
920
|
+
}}>
|
|
921
|
+
{errorMessage || 'Failed to join waitlist. Try again.'}
|
|
922
|
+
</div>
|
|
923
|
+
)}
|
|
924
|
+
|
|
925
|
+
<button
|
|
926
|
+
type="submit"
|
|
927
|
+
disabled={status === 'loading'}
|
|
928
|
+
style={{
|
|
929
|
+
width: '100%',
|
|
930
|
+
background: status === 'loading' ? '#52525b' : 'linear-gradient(90deg, #f59e0b 0%, #ef4444 100%)',
|
|
931
|
+
color: '#000',
|
|
932
|
+
padding: '14px 32px',
|
|
933
|
+
borderRadius: '8px',
|
|
934
|
+
fontWeight: 600,
|
|
935
|
+
fontSize: '1rem',
|
|
936
|
+
border: 'none',
|
|
937
|
+
cursor: status === 'loading' ? 'not-allowed' : 'pointer'
|
|
938
|
+
}}
|
|
939
|
+
>
|
|
940
|
+
{status === 'loading' ? 'Joining...' : 'Join the Waitlist'}
|
|
941
|
+
</button>
|
|
942
|
+
</form>
|
|
943
|
+
);
|
|
944
|
+
}
|