thumbgate 1.23.0 → 1.23.2
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-plugin/marketplace.json +5 -5
- package/.claude-plugin/plugin.json +2 -2
- package/.well-known/llms.txt +26 -11
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +69 -34
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +57 -16
- package/bin/postinstall.js +11 -22
- package/config/gate-templates.json +72 -0
- package/config/github-about.json +1 -1
- package/config/post-deploy-marketing-pages.json +10 -0
- package/package.json +6 -6
- package/public/agent-manager.html +3 -3
- package/public/agents-cost-savings.html +3 -3
- package/public/ai-malpractice-prevention.html +726 -149
- package/public/blog.html +3 -3
- package/public/codex-enterprise.html +3 -3
- package/public/codex-plugin.html +4 -4
- package/public/compare.html +6 -6
- package/public/dashboard.html +211 -126
- package/public/guide.html +5 -5
- package/public/index.html +187 -47
- package/public/learn.html +24 -10
- package/public/lessons.html +2 -2
- package/public/numbers.html +6 -6
- package/public/pricing.html +6 -5
- package/public/pro.html +23 -0
- package/scripts/billing.js +17 -0
- package/scripts/commercial-offer.js +75 -0
- package/scripts/dashboard.js +53 -1
- package/scripts/gates-engine.js +3 -3
- package/scripts/plausible-server-events.js +2 -1
- package/scripts/rate-limiter.js +16 -12
- package/scripts/seo-gsd.js +167 -1
- package/scripts/telemetry-analytics.js +310 -0
- package/scripts/visitor-journey.js +172 -0
- package/src/api/server.js +65 -29
- package/adapters/chatgpt/openapi.yaml +0 -1705
package/public/lessons.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@context": "https://schema.org",
|
|
13
13
|
"@type": "WebPage",
|
|
14
14
|
"name": "ThumbGate Lessons Learned",
|
|
15
|
-
"url": "https://thumbgate
|
|
15
|
+
"url": "https://thumbgate.ai/lessons",
|
|
16
16
|
"dateModified": "2026-04-20",
|
|
17
17
|
"author": {
|
|
18
18
|
"@type": "Person",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"publisher": {
|
|
27
27
|
"@type": "Organization",
|
|
28
28
|
"name": "ThumbGate",
|
|
29
|
-
"url": "https://thumbgate
|
|
29
|
+
"url": "https://thumbgate.ai"
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
</script>
|
package/public/numbers.html
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
<meta property="og:title" content="ThumbGate — The Numbers">
|
|
11
11
|
<meta property="og:description" content="Generated first-party operational snapshot: configured gates, recorded interventions, and explicit zero-evidence caveats.">
|
|
12
12
|
<meta property="og:type" content="website">
|
|
13
|
-
<meta property="og:url" content="https://thumbgate
|
|
13
|
+
<meta property="og:url" content="https://thumbgate.ai/numbers">
|
|
14
14
|
<meta name="twitter:card" content="summary_large_image">
|
|
15
|
-
<link rel="canonical" href="https://thumbgate
|
|
15
|
+
<link rel="canonical" href="https://thumbgate.ai/numbers">
|
|
16
16
|
<link rel="icon" type="image/png" href="/thumbgate-icon.png">
|
|
17
17
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
18
18
|
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"alternateName": "thumbgate",
|
|
26
26
|
"applicationCategory": "DeveloperApplication",
|
|
27
27
|
"operatingSystem": "Cross-platform, Node.js >=18.18.0",
|
|
28
|
-
"softwareVersion": "1.23.
|
|
29
|
-
"url": "https://thumbgate
|
|
28
|
+
"softwareVersion": "1.23.2",
|
|
29
|
+
"url": "https://thumbgate.ai/numbers",
|
|
30
30
|
"dateModified": "2026-05-07",
|
|
31
31
|
"creator": {
|
|
32
32
|
"@type": "Person",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@type": "Dataset",
|
|
47
47
|
"name": "ThumbGate Operational Snapshot",
|
|
48
48
|
"description": "First-party operational snapshot from the ThumbGate pre-action check runtime: configured checks, recorded block/warn events, estimated token savings from recorded blocks, and Bayes error rate when the sample supports it.",
|
|
49
|
-
"url": "https://thumbgate
|
|
49
|
+
"url": "https://thumbgate.ai/numbers",
|
|
50
50
|
"license": "https://opensource.org/licenses/MIT",
|
|
51
51
|
"creator": {
|
|
52
52
|
"@type": "Person",
|
|
@@ -202,7 +202,7 @@
|
|
|
202
202
|
<main class="container">
|
|
203
203
|
<h1>The Numbers</h1>
|
|
204
204
|
<p class="subtitle">Generated first-party operational snapshot from the ThumbGate runtime. This is not customer traction, install volume, revenue, or proof that a configured gate has fired.</p>
|
|
205
|
-
<div class="freshness">Updated: 2026-05-07 · Version 1.23.
|
|
205
|
+
<div class="freshness">Updated: 2026-05-07 · Version 1.23.2</div>
|
|
206
206
|
<div class="truth-note"><strong>Read this first:</strong> configured checks are inventory. Recorded blocks and warnings are usage evidence. This snapshot currently reports 0 recorded hard-block event(s) and 0 recorded warning event(s).</div>
|
|
207
207
|
|
|
208
208
|
<h2>Gate enforcement</h2>
|
package/public/pricing.html
CHANGED
|
@@ -12,6 +12,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
12
12
|
<meta property="og:url" content="__APP_ORIGIN__/pricing">
|
|
13
13
|
<meta property="og:image" content="/og.png">
|
|
14
14
|
<link rel="canonical" href="__APP_ORIGIN__/pricing">
|
|
15
|
+
<link rel="alternate" type="text/markdown" title="ThumbGate LLM context" href="__APP_ORIGIN__/llm-context.md">
|
|
15
16
|
<link rel="icon" type="image/png" href="/thumbgate-icon.png">
|
|
16
17
|
<link rel="apple-touch-icon" href="/assets/brand/thumbgate-mark.svg">
|
|
17
18
|
|
|
@@ -49,7 +50,7 @@ __GA_BOOTSTRAP__
|
|
|
49
50
|
"@context": "https://schema.org",
|
|
50
51
|
"@type": "FAQPage",
|
|
51
52
|
"mainEntity": [
|
|
52
|
-
{ "@type": "Question", "name": "What does Pro add over the free CLI?", "acceptedAnswer": { "@type": "Answer", "text": "Free gives you
|
|
53
|
+
{ "@type": "Question", "name": "What does Pro add over the free CLI?", "acceptedAnswer": { "@type": "Answer", "text": "Free gives you 5 captures/day and 3 active rules, running entirely on your machine. Pro is the hosted layer: unlimited captures, unlimited rules, lesson sync across machines, a dashboard without self-hosting, managed adapter updates, and DPO export. You're paying for infrastructure we run, not features we hide." } },
|
|
53
54
|
{ "@type": "Question", "name": "Does ThumbGate send my code to the cloud?", "acceptedAnswer": { "@type": "Answer", "text": "No. The CLI is local-first — no data leaves your machine. Pro and Team add hosted sync for dashboards and shared lessons, but your source code stays local." } },
|
|
54
55
|
{ "@type": "Question", "name": "When should I pick Team over Pro?", "acceptedAnswer": { "@type": "Answer", "text": "When one engineer's correction should protect the whole team. Team shares the lesson database across seats so a fix in one repo prevents the same mistake in every repo." } },
|
|
55
56
|
{ "@type": "Question", "name": "Can I cancel anytime?", "acceptedAnswer": { "@type": "Answer", "text": "Yes. Pro and Team are month-to-month with a 7-day refund window on the first charge. Cancel from the billing portal and your subscription ends at the period close." } }
|
|
@@ -237,8 +238,8 @@ __GA_BOOTSTRAP__
|
|
|
237
238
|
<div class="price">$0</div>
|
|
238
239
|
<div class="price-sub">Block repeated mistakes daily. Forever free for solo devs.</div>
|
|
239
240
|
<ul>
|
|
240
|
-
<li>
|
|
241
|
-
<li>Up to
|
|
241
|
+
<li>5 feedback captures/day (25 total) — enough to see the value</li>
|
|
242
|
+
<li>Up to 3 active prevention rules</li>
|
|
242
243
|
<li>All MCP integrations (Claude Code, Cursor, Codex, Gemini, Amp)</li>
|
|
243
244
|
<li>PreToolUse hook blocking with built-in safety checks</li>
|
|
244
245
|
<li>Runs 100% local — no account, no signup, no data leaves your machine</li>
|
|
@@ -257,7 +258,7 @@ __GA_BOOTSTRAP__
|
|
|
257
258
|
<li><strong>Hosted lesson sync</strong> — corrections follow you across machines, no manual export</li>
|
|
258
259
|
<li><strong>Managed adapter matrix</strong> — we track runtime changes in Claude Code, Cursor, Codex, Gemini, Amp so you don't</li>
|
|
259
260
|
<li><strong>Hosted dashboard</strong> — see every blocked action, every rule that fired, without running your own server</li>
|
|
260
|
-
<li><strong>Unlimited prevention rules</strong> — free caps at
|
|
261
|
+
<li><strong>Unlimited prevention rules</strong> — free caps at 3 auto-promoted rules</li>
|
|
261
262
|
<li><strong>DPO + HuggingFace export</strong> — training data from your real corrections</li>
|
|
262
263
|
<li><strong>Auto-connect</strong> — new agent surfaces appear automatically after setup</li>
|
|
263
264
|
<li>7-day refund window. Cancel anytime.</li>
|
|
@@ -296,7 +297,7 @@ __GA_BOOTSTRAP__
|
|
|
296
297
|
<div class="faq-list">
|
|
297
298
|
<div class="faq-item">
|
|
298
299
|
<div class="faq-q">What does Pro add over the free CLI?</div>
|
|
299
|
-
<div class="faq-a">Free gives you
|
|
300
|
+
<div class="faq-a">Free gives you 5 captures/day and 3 active rules, running entirely on your machine. Pro is the hosted layer: unlimited captures, unlimited rules, lesson sync across machines, a dashboard without self-hosting, managed adapter updates, and DPO export. You're paying for infrastructure we run, not features we hide.</div>
|
|
300
301
|
</div>
|
|
301
302
|
<div class="faq-item">
|
|
302
303
|
<div class="faq-q">Does ThumbGate send my code to the cloud?</div>
|
package/public/pro.html
CHANGED
|
@@ -11,6 +11,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
11
11
|
<meta property="og:type" content="website">
|
|
12
12
|
<meta property="og:url" content="__APP_ORIGIN__/pro">
|
|
13
13
|
<link rel="canonical" href="__APP_ORIGIN__/pro">
|
|
14
|
+
<link rel="alternate" type="text/markdown" title="ThumbGate LLM context" href="__APP_ORIGIN__/llm-context.md">
|
|
14
15
|
<link rel="icon" type="image/png" href="/thumbgate-icon.png">
|
|
15
16
|
<link rel="apple-touch-icon" href="/assets/brand/thumbgate-mark.svg">
|
|
16
17
|
<meta property="og:image" content="/og.png">
|
|
@@ -815,6 +816,28 @@ __GA_BOOTSTRAP__
|
|
|
815
816
|
</div>
|
|
816
817
|
</section>
|
|
817
818
|
|
|
819
|
+
<section class="section" id="deterministic-loop">
|
|
820
|
+
<div class="container">
|
|
821
|
+
<div class="section-label">Why Pro now</div>
|
|
822
|
+
<h2 class="section-title">Black-box thumbs do not prove prevention. Pro gives the operator an audit loop.</h2>
|
|
823
|
+
<p class="section-intro">Native rating buttons can tell a vendor that an answer felt wrong. ThumbGate Pro gives you the operational record: the correction, the lesson, the rule, the blocked tool call, and the export path.</p>
|
|
824
|
+
<div class="grid-3">
|
|
825
|
+
<div class="feature-card">
|
|
826
|
+
<h3>Inspectable memory</h3>
|
|
827
|
+
<p>Search the exact lesson that came from a thumbs-down and see whether it is still active, warning-only, or blocking.</p>
|
|
828
|
+
</div>
|
|
829
|
+
<div class="feature-card">
|
|
830
|
+
<h3>Deterministic checks</h3>
|
|
831
|
+
<p>The enforcement layer evaluates tool name, arguments, working directory, command shape, confidence, and required evidence before the action runs.</p>
|
|
832
|
+
</div>
|
|
833
|
+
<div class="feature-card">
|
|
834
|
+
<h3>Exportable proof</h3>
|
|
835
|
+
<p>Take the same correction history into JSONL, DPO export, review packets, and team rollout conversations instead of trusting hidden memory.</p>
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
838
|
+
</div>
|
|
839
|
+
</section>
|
|
840
|
+
|
|
818
841
|
<section class="section" id="why-pay">
|
|
819
842
|
<div class="container">
|
|
820
843
|
<div class="section-label">Why operators pay</div>
|
package/scripts/billing.js
CHANGED
|
@@ -51,6 +51,7 @@ const {
|
|
|
51
51
|
} = require('./analytics-window');
|
|
52
52
|
const { ensureParentDir } = require('./fs-utils');
|
|
53
53
|
const mailer = require('./mailer');
|
|
54
|
+
const { recordCheckoutFunnelEvent } = require('./plausible-server-events');
|
|
54
55
|
|
|
55
56
|
function loadWorkflowSprintIntakeModule() {
|
|
56
57
|
const modulePath = path.resolve(__dirname, 'workflow-sprint-intake.js');
|
|
@@ -3038,6 +3039,22 @@ async function handleWebhook(rawBody, signature) {
|
|
|
3038
3039
|
attribution,
|
|
3039
3040
|
});
|
|
3040
3041
|
}
|
|
3042
|
+
// Fire Plausible purchase event so the funnel poller can measure
|
|
3043
|
+
// end-to-end conversion: visitor → CTA → checkout → email → Stripe → purchase.
|
|
3044
|
+
// Fire-and-forget (never blocks the webhook response).
|
|
3045
|
+
void recordCheckoutFunnelEvent('purchase', {
|
|
3046
|
+
page: '/success',
|
|
3047
|
+
props: {
|
|
3048
|
+
sessionId: session.id,
|
|
3049
|
+
customerId,
|
|
3050
|
+
traceId: traceId || '',
|
|
3051
|
+
packId: packId || '',
|
|
3052
|
+
amount: session.amount_total != null ? String(session.amount_total) : '',
|
|
3053
|
+
currency: session.currency || '',
|
|
3054
|
+
...attribution,
|
|
3055
|
+
},
|
|
3056
|
+
});
|
|
3057
|
+
|
|
3041
3058
|
return {
|
|
3042
3059
|
handled: true,
|
|
3043
3060
|
action: 'provisioned_api_key',
|
|
@@ -35,6 +35,78 @@ function normalizeSeatCount(value, fallback = TEAM_MIN_SEATS) {
|
|
|
35
35
|
return Math.max(TEAM_MIN_SEATS, Math.round(parsed));
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
function trackedProUrl(source = 'cli_receipt', content = 'value_receipt') {
|
|
39
|
+
try {
|
|
40
|
+
const url = new URL(PRO_MONTHLY_PAYMENT_LINK);
|
|
41
|
+
url.searchParams.set('utm_source', source);
|
|
42
|
+
url.searchParams.set('utm_medium', 'cli');
|
|
43
|
+
url.searchParams.set('utm_campaign', 'pro_conversion');
|
|
44
|
+
url.searchParams.set('utm_content', content);
|
|
45
|
+
return url.toString();
|
|
46
|
+
} catch (_) {
|
|
47
|
+
return PRO_MONTHLY_PAYMENT_LINK;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pluralize(count, singular, plural = `${singular}s`) {
|
|
52
|
+
return Number(count) === 1 ? singular : plural;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildCaptureReceipt({ signal, feedbackId, memoryId, actionType } = {}) {
|
|
56
|
+
const normalizedSignal = String(signal || '').toUpperCase() || 'UNKNOWN';
|
|
57
|
+
const lines = [
|
|
58
|
+
'',
|
|
59
|
+
'Value receipt',
|
|
60
|
+
'─'.repeat(50),
|
|
61
|
+
` Stored proof : ${normalizedSignal} feedback${feedbackId ? ` (${feedbackId})` : ''}`,
|
|
62
|
+
memoryId ? ` Local memory : ${memoryId}` : ' Local memory : saved locally',
|
|
63
|
+
actionType ? ` Rule pressure : ${actionType}` : ' Rule pressure : available for promotion',
|
|
64
|
+
' Free today : this proof protects this local machine',
|
|
65
|
+
' Pro sync : keep this lesson, rule, and dashboard synced across machines and agent runtimes',
|
|
66
|
+
' Next proof : npx thumbgate stats',
|
|
67
|
+
' Cost proof : npx thumbgate cost',
|
|
68
|
+
'',
|
|
69
|
+
` Solo Pro : ${PRO_PRICE_LABEL} for hosted sync, search, dashboard, and exports`,
|
|
70
|
+
` Upgrade : ${trackedProUrl('cli_capture_receipt', actionType || normalizedSignal.toLowerCase())}`,
|
|
71
|
+
` Team path : ${TEAM_PRICE_LABEL}; start with one repeated workflow failure`,
|
|
72
|
+
' https://thumbgate.ai/#workflow-sprint-intake',
|
|
73
|
+
'',
|
|
74
|
+
];
|
|
75
|
+
return lines.join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildStatsReceipt(stats = {}) {
|
|
79
|
+
const negatives = Number(stats.negatives || stats.totalNegative || 0);
|
|
80
|
+
const blocked = Number(stats.gatesBlocked || stats.blocked || 0);
|
|
81
|
+
const warned = Number(stats.gatesWarned || stats.warned || 0);
|
|
82
|
+
const gates = Number(stats.totalGates || 0);
|
|
83
|
+
const autoPromoted = Number(stats.autoPromotedGates || 0);
|
|
84
|
+
const hasFriction = negatives > 0 || blocked > 0 || warned > 0 || gates > 0;
|
|
85
|
+
if (!hasFriction) return '';
|
|
86
|
+
|
|
87
|
+
const interventions = blocked + warned;
|
|
88
|
+
const lines = [
|
|
89
|
+
'',
|
|
90
|
+
'Paid-intent next step',
|
|
91
|
+
'─'.repeat(50),
|
|
92
|
+
];
|
|
93
|
+
if (interventions > 0) {
|
|
94
|
+
lines.push(` Proof already seen : ${interventions} gate ${pluralize(interventions, 'intervention')}`);
|
|
95
|
+
}
|
|
96
|
+
if (gates > 0) {
|
|
97
|
+
lines.push(` Active prevention : ${gates} ${pluralize(gates, 'gate')} (${autoPromoted} auto-promoted)`);
|
|
98
|
+
}
|
|
99
|
+
if (negatives > 0) {
|
|
100
|
+
lines.push(` Failure pressure : ${negatives} negative ${pluralize(negatives, 'signal')}`);
|
|
101
|
+
}
|
|
102
|
+
lines.push(' Show the buyer : npx thumbgate cost');
|
|
103
|
+
lines.push(' Pro sync value : keep these lessons/rules visible across laptops, CI, containers, and agent runtimes');
|
|
104
|
+
lines.push(` Solo Pro : ${trackedProUrl('cli_stats_receipt', 'proof_seen')}`);
|
|
105
|
+
lines.push(' Team workflow : https://thumbgate.ai/#workflow-sprint-intake');
|
|
106
|
+
lines.push('');
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
38
110
|
module.exports = {
|
|
39
111
|
PRO_MONTHLY_PAYMENT_LINK,
|
|
40
112
|
PRO_ANNUAL_PAYMENT_LINK,
|
|
@@ -51,4 +123,7 @@ module.exports = {
|
|
|
51
123
|
normalizePlanId,
|
|
52
124
|
normalizeBillingCycle,
|
|
53
125
|
normalizeSeatCount,
|
|
126
|
+
buildCaptureReceipt,
|
|
127
|
+
buildStatsReceipt,
|
|
128
|
+
trackedProUrl,
|
|
54
129
|
};
|
package/scripts/dashboard.js
CHANGED
|
@@ -967,6 +967,20 @@ function computeAnalyticsSummary(feedbackDir, options = {}) {
|
|
|
967
967
|
return {
|
|
968
968
|
window: telemetry.window || analyticsWindow,
|
|
969
969
|
telemetry,
|
|
970
|
+
firstPartyTrafficQuality: telemetry.trafficQuality || {
|
|
971
|
+
rawEvents: telemetry.totalEvents || 0,
|
|
972
|
+
externalEvents: 0,
|
|
973
|
+
excludedEvents: 0,
|
|
974
|
+
exclusionRate: 0,
|
|
975
|
+
byAudience: {},
|
|
976
|
+
byExclusionReason: {},
|
|
977
|
+
external: {
|
|
978
|
+
uniqueVisitors: 0,
|
|
979
|
+
pageViews: 0,
|
|
980
|
+
checkoutStarts: 0,
|
|
981
|
+
},
|
|
982
|
+
verdict: 'missing',
|
|
983
|
+
},
|
|
970
984
|
funnel: {
|
|
971
985
|
visitors: uniqueVisitors,
|
|
972
986
|
sessions: telemetry.visitors ? telemetry.visitors.uniqueSessions || 0 : 0,
|
|
@@ -1149,16 +1163,42 @@ function computeInstrumentationReadiness(analytics, billing) {
|
|
|
1149
1163
|
const coverage = billing && billing.coverage ? billing.coverage : {};
|
|
1150
1164
|
const telemetry = analytics.telemetry || {};
|
|
1151
1165
|
const visitors = telemetry.visitors || {};
|
|
1166
|
+
const quality = telemetry.trafficQuality || analytics.firstPartyTrafficQuality || {};
|
|
1167
|
+
const external = quality.external || {};
|
|
1152
1168
|
const cli = telemetry.cli || {};
|
|
1169
|
+
const plausibleExportConfigured = Boolean(
|
|
1170
|
+
process.env.PLAUSIBLE_API_KEY && (process.env.PLAUSIBLE_SITE_ID || process.env.PLAUSIBLE_DOMAIN)
|
|
1171
|
+
);
|
|
1172
|
+
const posthogExportConfigured = Boolean(
|
|
1173
|
+
(process.env.POSTHOG_PERSONAL_API_KEY || process.env.POSTHOG_API_KEY) && process.env.POSTHOG_PROJECT_ID
|
|
1174
|
+
);
|
|
1175
|
+
const ga4ExportConfigured = Boolean(
|
|
1176
|
+
process.env.GA4_PROPERTY_ID && (process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GOOGLE_CLIENT_EMAIL)
|
|
1177
|
+
);
|
|
1178
|
+
const dashboardGradeExportReady = plausibleExportConfigured || posthogExportConfigured || ga4ExportConfigured;
|
|
1153
1179
|
|
|
1154
1180
|
return {
|
|
1155
|
-
plausibleConfigured: /plausible\.io\/js\/script
|
|
1181
|
+
plausibleConfigured: /plausible\.io\/js\/script(?:\.tagged-events)?\.js|\/js\/analytics\.js/.test(landingPage),
|
|
1156
1182
|
ga4Configured: Boolean(runtimeConfig.gaMeasurementId),
|
|
1157
1183
|
googleSearchConsoleConfigured: Boolean(runtimeConfig.googleSiteVerification),
|
|
1158
1184
|
softwareApplicationSchemaPresent: /"@type": "SoftwareApplication"/.test(landingPage),
|
|
1159
1185
|
faqSchemaPresent: /"@type": "FAQPage"/.test(landingPage),
|
|
1160
1186
|
telemetryEventsPresent: (telemetry.totalEvents || 0) > 0,
|
|
1161
1187
|
uniqueVisitorsTracked: visitors.uniqueVisitors || 0,
|
|
1188
|
+
rawTelemetryEvents: quality.rawEvents || telemetry.totalEvents || 0,
|
|
1189
|
+
externalTelemetryEvents: quality.externalEvents || 0,
|
|
1190
|
+
excludedTelemetryEvents: quality.excludedEvents || 0,
|
|
1191
|
+
externalVisitorsTracked: external.uniqueVisitors || 0,
|
|
1192
|
+
externalPageViewsTracked: external.pageViews || 0,
|
|
1193
|
+
externalCheckoutStartsTracked: external.checkoutStarts || 0,
|
|
1194
|
+
externalVisitorPathsTracked: Array.isArray(external.visitorPaths) ? external.visitorPaths.length : 0,
|
|
1195
|
+
internalTestPollutionRate: quality.exclusionRate || 0,
|
|
1196
|
+
trafficQualityVerdict: quality.verdict || 'missing',
|
|
1197
|
+
topExcludedTrafficReason: quality.topExclusionReason || null,
|
|
1198
|
+
plausibleExportConfigured,
|
|
1199
|
+
posthogExportConfigured,
|
|
1200
|
+
ga4ExportConfigured,
|
|
1201
|
+
dashboardGradeExportReady,
|
|
1162
1202
|
cliInstallsTracked: cli.uniqueInstalls || 0,
|
|
1163
1203
|
funnelEventsPresent: (analytics.reconciliation.telemetryCheckoutStarts || 0) > 0,
|
|
1164
1204
|
seoSignalsPresent: (analytics.seo.landingViews || 0) > 0,
|
|
@@ -1865,8 +1905,10 @@ function printDashboard(data) {
|
|
|
1865
1905
|
console.log('');
|
|
1866
1906
|
console.log('\uD83D\uDCBC Growth Analytics');
|
|
1867
1907
|
console.log(` Unique Visitors : ${analytics.trafficMetrics.visitors}`);
|
|
1908
|
+
console.log(` External Visitors: ${instrumentation.externalVisitorsTracked}`);
|
|
1868
1909
|
console.log(` Sessions : ${analytics.trafficMetrics.sessions}`);
|
|
1869
1910
|
console.log(` Page Views : ${analytics.trafficMetrics.pageViews}`);
|
|
1911
|
+
console.log(` External Views : ${instrumentation.externalPageViewsTracked}`);
|
|
1870
1912
|
console.log(` CTA Clicks : ${analytics.trafficMetrics.ctaClicks}`);
|
|
1871
1913
|
console.log(` Leads : ${analytics.funnel.acquisitionLeads}`);
|
|
1872
1914
|
console.log(` Sprint Leads : ${analytics.pipeline.workflowSprintLeads.total}`);
|
|
@@ -1877,6 +1919,7 @@ function printDashboard(data) {
|
|
|
1877
1919
|
console.log(` Booked Revenue : $${(analytics.revenue.bookedRevenueCents / 100).toFixed(2)}`);
|
|
1878
1920
|
console.log(` Matched Journeys : ${analytics.reconciliation.matchedPaidOrders}/${analytics.reconciliation.telemetryCheckoutStarts}`);
|
|
1879
1921
|
console.log(` Buyer Loss : ${analytics.buyerLoss.totalSignals}`);
|
|
1922
|
+
console.log(` Data Quality : ${analytics.firstPartyTrafficQuality.verdict} (${instrumentation.excludedTelemetryEvents}/${instrumentation.rawTelemetryEvents} excluded)`);
|
|
1880
1923
|
if (analytics.telemetry.visitors.topSource) {
|
|
1881
1924
|
console.log(` Top Source : ${analytics.telemetry.visitors.topSource.key} (${analytics.telemetry.visitors.topSource.count}\u00D7)`);
|
|
1882
1925
|
}
|
|
@@ -1896,6 +1939,15 @@ function printDashboard(data) {
|
|
|
1896
1939
|
console.log(` GA4 : ${instrumentation.ga4Configured ? 'configured' : 'missing'}`);
|
|
1897
1940
|
console.log(` Search Console : ${instrumentation.googleSearchConsoleConfigured ? 'configured' : 'missing'}`);
|
|
1898
1941
|
console.log(` Telemetry Events : ${instrumentation.telemetryEventsPresent ? instrumentation.uniqueVisitorsTracked : 0} visitors`);
|
|
1942
|
+
console.log(` Clean Visitors : ${instrumentation.externalVisitorsTracked} external (${Math.round((instrumentation.internalTestPollutionRate || 0) * 100)}% internal/test/bot events)`);
|
|
1943
|
+
console.log(` Clean Paths : ${instrumentation.externalVisitorPathsTracked} first-party paths`);
|
|
1944
|
+
if (instrumentation.topExcludedTrafficReason) {
|
|
1945
|
+
console.log(` Top Exclusion : ${instrumentation.topExcludedTrafficReason.key} (${instrumentation.topExcludedTrafficReason.count}\u00D7)`);
|
|
1946
|
+
}
|
|
1947
|
+
console.log(` Plausible Export : ${instrumentation.plausibleExportConfigured ? 'configured' : 'missing API credentials'}`);
|
|
1948
|
+
console.log(` PostHog Export : ${instrumentation.posthogExportConfigured ? 'configured' : 'missing API credentials'}`);
|
|
1949
|
+
console.log(` GA4 Export : ${instrumentation.ga4ExportConfigured ? 'configured' : 'missing API credentials'}`);
|
|
1950
|
+
console.log(` Visitor Paths : ${instrumentation.dashboardGradeExportReady ? 'export-ready' : 'not dashboard-grade in repo'}`);
|
|
1899
1951
|
console.log(` SEO Signals : ${instrumentation.seoSignalsPresent ? analytics.seo.landingViews : 0}`);
|
|
1900
1952
|
console.log(` Buyer Loss : ${instrumentation.buyerLossSignalsPresent ? analytics.buyerLoss.totalSignals : 0}`);
|
|
1901
1953
|
console.log(` Attribution : ${Math.round((instrumentation.trafficAttributionCoverage || 0) * 100)}% page-view coverage`);
|
package/scripts/gates-engine.js
CHANGED
|
@@ -1953,12 +1953,12 @@ function buildBlockActionProCta() {
|
|
|
1953
1953
|
if (totalBlocks < 5) return null; // Too early — let them experience the product
|
|
1954
1954
|
|
|
1955
1955
|
if (totalBlocks < 25) {
|
|
1956
|
-
return '\n\n💡 Pro:
|
|
1956
|
+
return '\n\n💡 Pro: keep this rule synced across laptops, CI, containers, and agent runtimes → thumbgate.ai/go/pro';
|
|
1957
1957
|
}
|
|
1958
1958
|
if (totalBlocks < 100) {
|
|
1959
|
-
return `\n\n💡 ${totalBlocks} actions blocked. Pro keeps rules
|
|
1959
|
+
return `\n\n💡 ${totalBlocks} actions blocked. Pro keeps these lessons/rules synced everywhere → thumbgate.ai/go/pro ($19/mo)`;
|
|
1960
1960
|
}
|
|
1961
|
-
return `\n\n💡 ${totalBlocks} mistakes caught. Your team could use
|
|
1961
|
+
return `\n\n💡 ${totalBlocks} mistakes caught. Your team could use shared hosted enforcement → thumbgate.ai/go/pro`;
|
|
1962
1962
|
} catch (_) {
|
|
1963
1963
|
return null;
|
|
1964
1964
|
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
const https = require('node:https');
|
|
27
27
|
|
|
28
|
-
const DEFAULT_PLAUSIBLE_DOMAIN = 'thumbgate
|
|
28
|
+
const DEFAULT_PLAUSIBLE_DOMAIN = 'thumbgate.ai';
|
|
29
29
|
const PLAUSIBLE_ENDPOINT = 'https://plausible.io/api/event';
|
|
30
30
|
const REQUEST_TIMEOUT_MS = 2_000;
|
|
31
31
|
|
|
@@ -142,6 +142,7 @@ const CHECKOUT_EVENT_NAMES = Object.freeze({
|
|
|
142
142
|
view: 'Checkout Pro Viewed',
|
|
143
143
|
emailSubmitted: 'Checkout Pro Email Submitted',
|
|
144
144
|
stripeRedirect: 'Checkout Pro Stripe Redirect Started',
|
|
145
|
+
purchase: 'Checkout Pro Purchase Completed',
|
|
145
146
|
});
|
|
146
147
|
|
|
147
148
|
function recordCheckoutFunnelEvent(stage, options = {}) {
|
package/scripts/rate-limiter.js
CHANGED
|
@@ -12,35 +12,37 @@ const {
|
|
|
12
12
|
const USAGE_FILE = path.join(process.env.HOME || '/tmp', '.thumbgate', 'usage-limits.json');
|
|
13
13
|
|
|
14
14
|
// ──────────────────────────────────────────────────────────
|
|
15
|
-
// Free tier:
|
|
16
|
-
//
|
|
17
|
-
//
|
|
15
|
+
// Free tier: tight enough to create upgrade pressure after
|
|
16
|
+
// real usage. Captures and rules are capped so heavy users
|
|
17
|
+
// hit the wall within the first week, not the first quarter.
|
|
18
18
|
// ──────────────────────────────────────────────────────────
|
|
19
19
|
const FREE_TIER_LIMITS = {
|
|
20
|
-
capture_feedback: { daily:
|
|
21
|
-
prevention_rules: { daily:
|
|
20
|
+
capture_feedback: { daily: 5, lifetime: 25, label: 'feedback captures (5/day, 25 total on free)' },
|
|
21
|
+
prevention_rules: { daily: 2, lifetime: 6, label: 'prevention rules generated (2/day on free)' },
|
|
22
22
|
recall: { daily: 0, lifetime: 0, label: 'recall queries (Pro only)' },
|
|
23
23
|
search_lessons: { daily: 0, lifetime: 0, label: 'lesson searches (Pro only)' },
|
|
24
24
|
search_thumbgate: { daily: 0, lifetime: 0, label: 'ThumbGate searches (Pro only)' },
|
|
25
25
|
commerce_recall: { daily: 0, lifetime: 0, label: 'commerce recalls (Pro only)' },
|
|
26
26
|
export_dpo: { daily: 0, lifetime: 0, label: 'DPO exports (Pro only)' },
|
|
27
27
|
export_databricks: { daily: 0, lifetime: 0, label: 'Databricks exports (Pro only)' },
|
|
28
|
-
construct_context_pack: { daily:
|
|
28
|
+
construct_context_pack: { daily: 3, lifetime: Infinity, label: 'context packs (3/day on free)' },
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
const FREE_TIER_MAX_GATES =
|
|
32
|
-
const FREE_TIER_DAILY_BLOCKS =
|
|
31
|
+
const FREE_TIER_MAX_GATES = 3; // 3 active prevention rules on free; Pro is unlimited
|
|
32
|
+
const FREE_TIER_DAILY_BLOCKS = 3; // 3 gate blocks/day on free; after limit, deny → warn + upgrade CTA
|
|
33
33
|
|
|
34
34
|
const UPGRADE_MESSAGE = `Pro: ${PRO_PRICE_LABEL} — unlimited rules, recall, lesson search, dashboard, and exports: ${PRO_MONTHLY_PAYMENT_LINK}\n Team: ${TEAM_PRICE_LABEL} after workflow qualification.`;
|
|
35
35
|
|
|
36
36
|
const PAYWALL_MESSAGES = {
|
|
37
|
-
|
|
37
|
+
capture_feedback: 'Free tier: 5 captures/day (25 total). Your feedback is stored locally — upgrade to capture unlimited.',
|
|
38
|
+
prevention_rules: 'Free tier includes 3 active prevention rules and 2 rule generations/day. Upgrade to Pro for unlimited rules.',
|
|
38
39
|
recall: 'Recall is a Pro feature. Your past feedback is stored locally — upgrade to search and reuse it.',
|
|
39
40
|
search_lessons: 'Lesson search is a Pro feature. Upgrade to find patterns in your agent\'s mistakes.',
|
|
41
|
+
construct_context_pack: 'Free tier: 3 context packs/day. Upgrade to Pro for unlimited.',
|
|
40
42
|
default: 'This feature requires Pro. Start Pro — card required; billed today.',
|
|
41
43
|
};
|
|
42
44
|
|
|
43
|
-
const TRIAL_DAYS =
|
|
45
|
+
const TRIAL_DAYS = 7;
|
|
44
46
|
|
|
45
47
|
function getInstallAgeDays() {
|
|
46
48
|
try {
|
|
@@ -91,7 +93,8 @@ function isProTier(authContext) {
|
|
|
91
93
|
const { isProLicensed } = require('./license');
|
|
92
94
|
if (isProLicensed()) return true;
|
|
93
95
|
} catch (_) {}
|
|
94
|
-
//
|
|
96
|
+
// 7-day reverse trial: new installs get full Pro access, then hit a clear
|
|
97
|
+
// hosted-sync/unlimited-rules pay moment while the product is still fresh.
|
|
95
98
|
if (isInTrialPeriod()) return true;
|
|
96
99
|
return false;
|
|
97
100
|
}
|
|
@@ -164,9 +167,10 @@ function checkLimit(action, authContext) {
|
|
|
164
167
|
|
|
165
168
|
// Check daily limit
|
|
166
169
|
if (dailyLimit !== Infinity && dailyCurrent >= dailyLimit) {
|
|
170
|
+
const paywallMsg = PAYWALL_MESSAGES[action] || PAYWALL_MESSAGES.default;
|
|
167
171
|
return {
|
|
168
172
|
allowed: false,
|
|
169
|
-
message: `Daily limit reached. ${UPGRADE_MESSAGE}`,
|
|
173
|
+
message: `Daily limit reached. ${paywallMsg}\n\n${UPGRADE_MESSAGE}`,
|
|
170
174
|
used: dailyCurrent,
|
|
171
175
|
limit: dailyLimit,
|
|
172
176
|
limitType: 'daily',
|