thumbgate 1.5.4 → 1.5.8

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate-marketplace",
3
- "version": "1.5.4",
3
+ "version": "1.5.8",
4
4
  "owner": {
5
5
  "name": "Igor Ganapolsky",
6
6
  "email": "ig5973700@gmail.com"
@@ -13,7 +13,7 @@
13
13
  "source": "npm",
14
14
  "package": "thumbgate"
15
15
  },
16
- "version": "1.5.4",
16
+ "version": "1.5.8",
17
17
  "author": {
18
18
  "name": "Igor Ganapolsky"
19
19
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thumbgate",
3
3
  "description": "Type 👍 or 👎 on any agent action. ThumbGate captures it, distills a lesson, and blocks the pattern from repeating. One thumbs-down = the agent physically cannot make that mistake again. 33 pre-action gates, budget enforcement, self-protection, and NIST/SOC2 compliance tags.",
4
- "version": "1.5.4",
4
+ "version": "1.5.8",
5
5
  "author": {
6
6
  "name": "Igor Ganapolsky"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.5.4",
3
+ "version": "1.5.8",
4
4
  "description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
5
5
  "homepage": "https://github.com/IgorGanapolsky/thumbgate",
6
6
  "transport": "stdio",
@@ -3,7 +3,7 @@
3
3
  - `chatgpt/openapi.yaml`: import into GPT Actions.
4
4
  - `gemini/function-declarations.json`: Gemini function-calling definitions.
5
5
  - `mcp/server-stdio.js`: underlying local MCP stdio server implementation.
6
- - `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.5.4 thumbgate serve`.
6
+ - `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.5.8 thumbgate serve`.
7
7
  - `codex/config.toml`: example Codex MCP profile section using the same version-pinned portable launcher.
8
8
  - `amp/skills/thumbgate-feedback/SKILL.md`: Amp skill template.
9
9
  - `opencode/opencode.json`: portable OpenCode MCP profile using the same version-pinned portable launcher.
@@ -2,13 +2,13 @@
2
2
  "mcpServers": {
3
3
  "thumbgate": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "thumbgate@1.5.4", "thumbgate", "serve"]
5
+ "args": ["--yes", "--package", "thumbgate@1.5.8", "thumbgate", "serve"]
6
6
  }
7
7
  },
8
8
  "hooks": {
9
9
  "preToolUse": {
10
10
  "command": "npx",
11
- "args": ["--yes", "--package", "thumbgate@1.5.4", "thumbgate", "gate-check"]
11
+ "args": ["--yes", "--package", "thumbgate@1.5.8", "thumbgate", "gate-check"]
12
12
  }
13
13
  }
14
14
  }
@@ -3,9 +3,9 @@
3
3
  # ~/.codex/config.json with the ThumbGate hooks and status line.
4
4
  [mcp_servers.thumbgate]
5
5
  command = "npx"
6
- args = ["--yes", "--package", "thumbgate@1.5.4", "thumbgate", "serve"]
6
+ args = ["--yes", "--package", "thumbgate@1.5.8", "thumbgate", "serve"]
7
7
 
8
8
  # Hard PreToolUse hook for Codex
9
9
  [hooks.pre_tool_use]
10
10
  command = "npx"
11
- args = ["--yes", "--package", "thumbgate@1.5.4", "thumbgate", "gate-check"]
11
+ args = ["--yes", "--package", "thumbgate@1.5.8", "thumbgate", "gate-check"]
@@ -146,7 +146,7 @@ const {
146
146
  finalizeSession: finalizeFeedbackSession,
147
147
  } = require('../../scripts/feedback-session');
148
148
 
149
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.5.4' };
149
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.5.8' };
150
150
  const COMMERCE_CATEGORIES = [
151
151
  'product_recommendation',
152
152
  'brand_compliance',
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.5.4",
10
+ "thumbgate@1.5.8",
11
11
  "thumbgate",
12
12
  "serve"
13
13
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.5.4",
3
+ "version": "1.5.8",
4
4
  "description": "Self-improving agent governance: type thumbs-up or thumbs-down on any AI agent action. ThumbGate turns every mistake into a prevention rule and blocks the pattern from repeating. One thumbs-down, never again. 33 pre-action gates, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
5
5
  "homepage": "https://thumbgate-production.up.railway.app",
6
6
  "repository": {
@@ -248,7 +248,7 @@
248
248
  "trace:eval": "node scripts/decision-trace.js eval",
249
249
  "social:reply-monitor": "node scripts/social-reply-monitor.js",
250
250
  "social:reply-monitor:dry": "node scripts/social-reply-monitor.js --dry-run",
251
- "test": "npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:zernio && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:check-congruence && npm run test:tool-registry && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:plan-gate && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:bot-detector && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-bot-guard && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:prompt-eval && npm run test:demo-voiceover && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity",
251
+ "test": "npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:session-analyzer && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:zernio && npm run test:post-video && npm run test:post-everywhere-instagram && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:check-congruence && npm run test:tool-registry && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:belief-update && npm run test:hosted-config && npm run test:operational-summary && npm run test:operator-key-auth && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:plan-gate && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:bot-detector && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:semantic-dedup && npm run test:fs-utils && npm run test:cli-schema && npm run test:explore && npm run test:lesson-reranker && npm run test:lesson-retrieval && npm run test:cross-encoder && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-post-hourly && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:budget && npm run test:quick-start && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets && npm run test:ai-search-visibility && npm run test:perplexity && npm run test:security-scanner && npm run test:llm-client && npm run test:managed-lesson-agent && npm run test:self-distill && npm run test:meta-agent && npm run test:harness-selector && npm run test:thumbgate-bench && npm run test:seo-guides && npm run test:enforcement-loop && npm run test:cli-agent-experience && npm run test:bot-detection && npm run test:checkout-bot-guard && npm run test:session-health && npm run test:session-episodes && npm run test:spec-gate && npm run test:decision-trace && npm run test:dashboard-insights && npm run test:prompt-eval && npm run test:demo-voiceover && npm run test:gate-coherence && npm run test:gate-eval && npm run test:high-roi && npm run test:public-static-assets && npm run test:token-savings && npm run test:workflow-gate-checkpoint && npm run test:lesson-export-import && npm run test:landing-page-claims && npm run test:dashboard-deeplink-e2e && npm run test:public-package-parity && npm run test:token-savings-dashboard && npm run test:cursor-wiring",
252
252
  "test:session-health": "node --test tests/session-health-sensor.test.js",
253
253
  "test:session-episodes": "node --test tests/session-episode-store.test.js",
254
254
  "test:spec-gate": "node --test tests/spec-gate.test.js",
@@ -485,7 +485,11 @@
485
485
  "test:lesson-export-import": "node --test tests/lesson-export-import.test.js",
486
486
  "test:landing-page-claims": "node --test tests/landing-page-claims.test.js",
487
487
  "test:dashboard-deeplink-e2e": "node --test tests/dashboard-deeplink-e2e.test.js",
488
- "test:public-package-parity": "node --test tests/public-package-parity.test.js"
488
+ "test:public-package-parity": "node --test tests/public-package-parity.test.js",
489
+ "prepare": "bash bin/install-hooks.sh >/dev/null 2>&1 || true",
490
+ "install:hooks": "bash bin/install-hooks.sh",
491
+ "test:token-savings-dashboard": "node --test tests/token-savings-dashboard.test.js",
492
+ "test:cursor-wiring": "node --test tests/cursor-wiring.test.js"
489
493
  },
490
494
  "keywords": [
491
495
  "mcp",
@@ -330,6 +330,21 @@
330
330
  <h2>📊 Feedback Insights &amp; Lesson Pipeline</h2>
331
331
  <p class="template-summary">How your thumbs-up/down signals turn into lessons that prevent repeat mistakes.</p>
332
332
 
333
+ <!-- Token Savings — computed from real gate-block counts -->
334
+ <div class="panel" id="tokenSavingsPanel" style="margin-bottom:24px;background:linear-gradient(135deg,rgba(74,222,128,0.04),rgba(34,211,238,0.03));border-color:rgba(74,222,128,0.25);">
335
+ <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap;">
336
+ <div style="flex:1;min-width:240px;">
337
+ <h3 style="margin-bottom:6px;">💸 Estimated tokens saved</h3>
338
+ <div style="font-size:12px;color:var(--text-muted);line-height:1.55;">Computed from your real blocked-action count × 2,000 input + 600 output tokens per avoided retry, priced at a Sonnet-heavy blend (80% Sonnet 4.5 / 15% Opus 4.6 / 5% Haiku 4.5). Conservative estimate — actual savings may be higher.</div>
339
+ </div>
340
+ <div style="text-align:right;min-width:220px;">
341
+ <div id="tokenSavingsDollars" style="font-size:40px;font-weight:800;color:#4ade80;letter-spacing:-0.02em;line-height:1;">—</div>
342
+ <div id="tokenSavingsTokens" style="font-size:13px;color:var(--text-muted);margin-top:6px;">— tokens · from <span id="tokenSavingsBlocks">—</span> blocked calls</div>
343
+ </div>
344
+ </div>
345
+ <div id="tokenSavingsSource" style="margin-top:12px;padding-top:12px;border-top:1px solid var(--border);font-size:11px;color:var(--text-muted);line-height:1.6;"></div>
346
+ </div>
347
+
333
348
  <!-- Pipeline Flow -->
334
349
  <div class="panel" style="margin-bottom:24px;">
335
350
  <h3 style="margin-bottom:16px;">Feedback → Lesson Pipeline</h3>
@@ -1220,12 +1235,49 @@ var lessonChart = null;
1220
1235
  var gateAuditChart = null;
1221
1236
 
1222
1237
  function renderInsights(data) {
1238
+ renderTokenSavings(data.tokenSavings, data.gateStats || {});
1223
1239
  renderPipeline(data.lessonPipeline || {});
1224
1240
  renderFeedbackTrendChart(data.feedbackTimeSeries || {});
1225
1241
  renderLessonTrendChart(data.feedbackTimeSeries || {});
1226
1242
  renderGateAuditChartFromData(data.gateAudit || {});
1227
1243
  }
1228
1244
 
1245
+ /**
1246
+ * Render token-savings panel from /v1/dashboard.tokenSavings.
1247
+ * Only shows a dollar figure when it's backed by real gate blocks.
1248
+ * Otherwise shows an honest "$0.00 — no blocks yet" state, NEVER
1249
+ * a marketing placeholder.
1250
+ */
1251
+ function renderTokenSavings(savings, gateStats) {
1252
+ var panel = document.getElementById('tokenSavingsPanel');
1253
+ if (!panel) return;
1254
+ var dollarsEl = document.getElementById('tokenSavingsDollars');
1255
+ var tokensEl = document.getElementById('tokenSavingsTokens');
1256
+ var blocksEl = document.getElementById('tokenSavingsBlocks');
1257
+ var sourceEl = document.getElementById('tokenSavingsSource');
1258
+
1259
+ if (!savings || typeof savings.dollarsSaved !== 'number') {
1260
+ dollarsEl.textContent = '$0.00';
1261
+ tokensEl.innerHTML = 'No blocked calls yet — estimate will appear after your first gate fires';
1262
+ if (blocksEl) blocksEl.textContent = '0';
1263
+ sourceEl.textContent = '';
1264
+ return;
1265
+ }
1266
+
1267
+ dollarsEl.textContent = savings.dollarsSavedDisplay || ('$' + Number(savings.dollarsSaved).toFixed(2));
1268
+ tokensEl.innerHTML = (savings.tokensSavedDisplay || '0') + ' tokens · from <span id="tokenSavingsBlocks">' +
1269
+ (savings.blockedCalls || 0) + '</span> blocked calls';
1270
+
1271
+ var mix = savings.modelMix || {};
1272
+ var mixParts = [];
1273
+ for (var m in mix) { mixParts.push(Math.round(mix[m] * 100) + '% ' + m); }
1274
+ var blend = savings.blendedPricePer1M || { input: 0, output: 0 };
1275
+ sourceEl.textContent =
1276
+ 'Methodology: blended ' + mixParts.join(' / ') +
1277
+ ' at $' + blend.input.toFixed(2) + '/1M input + $' + blend.output.toFixed(2) + '/1M output. ' +
1278
+ 'Data source: actual blocked count from gate-stats.json. No sample numbers.';
1279
+ }
1280
+
1229
1281
  function renderPipeline(pipeline) {
1230
1282
  var stages = pipeline.stages || [];
1231
1283
  var rates = pipeline.rates || {};
package/public/index.html CHANGED
@@ -283,9 +283,13 @@ __GA_BOOTSTRAP__
283
283
  .hero p { font-size: 17px; color: var(--text-muted); max-width: 520px; margin: 0 auto 36px; line-height: 1.6; }
284
284
  .hero-persona { font-size: 15px; color: var(--cyan); max-width: 600px; margin: 0 auto 20px; line-height: 1.5; font-weight: 500; }
285
285
  .hero-signals { display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; margin: 0 auto 28px; max-width: 760px; }
286
- .signal-pill { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--bg-raised); font-size: 13px; font-weight: 600; letter-spacing: -0.01em; }
286
+ .signal-pill { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--bg-raised); font-size: 13px; font-weight: 600; letter-spacing: -0.01em; text-decoration: none; cursor: pointer; transition: transform 0.12s ease, border-color 0.12s ease, background 0.12s ease; }
287
+ .signal-pill:hover { transform: translateY(-1px); border-color: rgba(34, 211, 238, 0.5); background: rgba(34, 211, 238, 0.06); }
288
+ .signal-pill:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
287
289
  .signal-pill.signal-up { border-color: rgba(74, 222, 128, 0.28); color: #b8f7c8; background: rgba(74, 222, 128, 0.08); }
290
+ .signal-pill.signal-up:hover { border-color: rgba(74, 222, 128, 0.6); background: rgba(74, 222, 128, 0.14); }
288
291
  .signal-pill.signal-down { border-color: rgba(248, 113, 113, 0.28); color: #ffc0c0; background: rgba(248, 113, 113, 0.08); }
292
+ .signal-pill.signal-down:hover { border-color: rgba(248, 113, 113, 0.6); background: rgba(248, 113, 113, 0.14); }
289
293
  .hero-actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; margin: 0 auto 16px; max-width: 760px; }
290
294
  .btn-pro-page { display: inline-flex; align-items: center; justify-content: center; padding: 11px 18px; background: var(--cyan); color: var(--bg); border-radius: 999px; text-decoration: none; font-size: 14px; font-weight: 700; box-shadow: 0 0 0 1px rgba(34,211,238,0.28), 0 12px 32px rgba(34,211,238,0.18); transition: opacity 0.15s, transform 0.15s; }
291
295
  .btn-pro-page:hover { opacity: 0.9; transform: translateY(-1px); }
@@ -528,6 +532,35 @@ __GA_BOOTSTRAP__
528
532
  <div class="hero-badge">● Your AI coding bill has a leak</div>
529
533
  <h1>Stop paying $ for the same AI mistake.</h1>
530
534
  <p style="font-size:18px;color:var(--text-muted);max-width:720px;margin:0 auto 20px;line-height:1.6;">Every retry loop, every hallucinated import, every "let me try a different approach" — those are billable tokens on every LLM vendor's bill. Thumbs-down once; ThumbGate blocks that exact mistake on every future call. Across Claude Code, Cursor, Codex, Gemini, Amp, OpenCode — any MCP-compatible agent, forever, including fast-moving vibe coding workflows.</p>
535
+
536
+ <!-- HERO PRICING CARD — visible in first viewport so $19/mo and $149/yr never get buried -->
537
+ <div id="pro-pitch" style="max-width:820px;margin:0 auto 32px;padding:24px 28px;background:linear-gradient(180deg,rgba(17,17,19,0.94) 0%,rgba(22,22,24,0.94) 100%);border:1px solid rgba(34,211,238,0.32);border-radius:20px;box-shadow:0 0 0 1px rgba(34,211,238,0.18),0 24px 64px rgba(0,0,0,0.35);text-align:left;">
538
+ <div style="display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:16px;">
539
+ <div>
540
+ <div style="display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;border:1px solid rgba(34,211,238,0.25);background:var(--cyan-dim);color:var(--cyan);font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;">Start 7-day free trial</div>
541
+ <h2 style="margin:10px 0 4px;font-size:22px;letter-spacing:-0.025em;line-height:1.2;">Go Pro — one correction, every agent, every session.</h2>
542
+ <p style="margin:0;font-size:13px;color:var(--text-muted);">Personal local dashboard · DPO export from real corrections · founder support on risky flows.</p>
543
+ </div>
544
+ </div>
545
+ <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px;">
546
+ <div style="display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 16px;border-radius:14px;border:1px solid rgba(34,211,238,0.35);background:rgba(34,211,238,0.06);">
547
+ <div style="text-align:left;">
548
+ <div style="font-size:22px;font-weight:800;letter-spacing:-0.03em;line-height:1.1;">$19<span style="font-size:13px;font-weight:600;color:var(--text-muted);">/mo</span></div>
549
+ <div style="font-size:12px;color:var(--text-muted);margin-top:2px;">Monthly Pro</div>
550
+ </div>
551
+ <a href="/checkout/pro?utm_source=website&utm_medium=hero_pricing_card&utm_campaign=pro_pack&cta_id=hero_pro_monthly&cta_placement=hero_pricing&plan_id=pro&landing_path=%2F" onclick="posthog.capture('hero_pricing_monthly_click',{cta:'choose_monthly'})" style="display:inline-flex;align-items:center;gap:6px;padding:11px 20px;background:var(--cyan);color:#041016;border-radius:999px;text-decoration:none;font-weight:700;font-size:14px;box-shadow:0 0 0 1px rgba(34,211,238,0.28),0 12px 32px rgba(34,211,238,0.18);white-space:nowrap;">Choose monthly →</a>
552
+ </div>
553
+ <div style="display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 16px;border-radius:14px;border:1px solid var(--border);background:rgba(255,255,255,0.02);">
554
+ <div style="text-align:left;">
555
+ <div style="font-size:22px;font-weight:800;letter-spacing:-0.03em;line-height:1.1;">$149<span style="font-size:13px;font-weight:600;color:var(--text-muted);">/yr</span> <span style="font-size:11px;color:#4ade80;background:rgba(74,222,128,0.14);padding:2px 7px;border-radius:999px;font-weight:700;vertical-align:middle;margin-left:4px;">SAVE 35%</span></div>
556
+ <div style="font-size:12px;color:var(--text-muted);margin-top:2px;">Annual Pro</div>
557
+ </div>
558
+ <a href="/checkout/pro?utm_source=website&utm_medium=hero_pricing_card&utm_campaign=pro_pack&cta_id=hero_pro_annual&cta_placement=hero_pricing&plan_id=pro&billing_cycle=annual&landing_path=%2F" onclick="posthog.capture('hero_pricing_annual_click',{cta:'choose_annual'})" style="display:inline-flex;align-items:center;gap:6px;padding:11px 20px;background:rgba(17,17,19,0.75);color:var(--text);border:1px solid var(--border);border-radius:999px;text-decoration:none;font-weight:700;font-size:14px;white-space:nowrap;">Choose annual →</a>
559
+ </div>
560
+ </div>
561
+ <p style="margin:14px 0 0;font-size:11px;color:var(--text-muted);text-align:center;">No credit card for 7-day trial · cancel anytime · your rules and captures stay local. <a href="/go/install" style="color:var(--cyan);text-decoration:none;">Prefer free? Install CLI →</a></p>
562
+ </div>
563
+
531
564
  <a href="/dashboard" class="hero-dashboard-preview" style="display:block;max-width:620px;margin:0 auto 28px;background:linear-gradient(135deg,#0d1220 0%,#121a2e 100%);border:1px solid rgba(34,211,238,0.35);border-radius:12px;padding:20px 24px;box-shadow:0 0 48px rgba(34,211,238,0.15);font-family:var(--mono);text-align:left;text-decoration:none;color:inherit;" title="Open your live dashboard">
532
565
  <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;font-size:11px;letter-spacing:0.08em;text-transform:uppercase;color:var(--text-muted);">
533
566
  <span>Your dashboard · <span style="color:#eab308;background:rgba(234,179,8,0.1);padding:2px 6px;border-radius:3px;letter-spacing:0.04em;">Sample</span></span>
@@ -554,9 +587,9 @@ __GA_BOOTSTRAP__
554
587
  })();
555
588
  </script>
556
589
  <div class="hero-signals">
557
- <div class="signal-pill signal-down">Block repeat hallucinations before the model sees them</div>
558
- <div class="signal-pill signal-up">Thumbs-down once, blocked forever, across every agent</div>
559
- <div class="signal-pill">CLI-first workflow governance with a live tokens-saved counter</div>
590
+ <a class="signal-pill signal-down" href="#how-it-works" title="See how gate interception works">Block repeat hallucinations before the model sees them</a>
591
+ <a class="signal-pill signal-up" href="#how-it-works" title="See the one-thumbs-down enforcement loop">Thumbs-down once, blocked forever, across every agent</a>
592
+ <a class="signal-pill" href="#install" title="Install the CLI">CLI-first workflow governance with a live tokens-saved counter</a>
560
593
  </div>
561
594
  <p class="hero-persona" style="display:none">For consultancies, platform teams, and AI product teams with one workflow owner, one repeated failure, and one buyer who needs proof before a wider rollout.</p>
562
595
  <div class="hero-actions" style="margin-top:32px;">
@@ -571,6 +604,7 @@ __GA_BOOTSTRAP__
571
604
  <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb" class="btn-gpt-page" target="_blank" rel="noopener" onclick="posthog.capture('hero_claude_extension_click',{cta:'install_claude_extension'})" style="font-size:12px;padding:8px 16px;background:#d97706;color:#fff;box-shadow:none;">Install Claude Extension</a>
572
605
  <a href="/go/github?utm_source=website&utm_medium=hero_cta&utm_campaign=github_repo&cta_id=hero_star_github&cta_placement=hero" target="_blank" rel="noopener" class="btn-free" style="display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:999px;font-size:12px;">⭐ Star on GitHub</a>
573
606
  <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" class="btn-install-link" target="_blank" rel="noopener" onclick="posthog.capture('hero_codex_plugin_click',{cta:'install_codex_plugin'})" style="font-size:11px;color:var(--text-muted);text-decoration:none;padding:6px 10px;">Install Codex plugin →</a>
607
+ <a href="https://github.com/IgorGanapolsky/ThumbGate/tree/main/plugins/cursor-marketplace" class="btn-install-link" target="_blank" rel="noopener" onclick="posthog.capture('hero_cursor_plugin_click',{cta:'install_cursor_plugin'})" style="font-size:11px;color:var(--text-muted);text-decoration:none;padding:6px 10px;">Install Cursor plugin →</a>
574
608
  <a href="/go/gpt?utm_source=website&utm_medium=hero_cta&utm_campaign=chatgpt_gpt&cta_id=hero_open_gpt&cta_placement=hero" class="btn-gpt-page" target="_blank" rel="noopener" onclick="posthog.capture('hero_cta_click',{cta:'open_gpt'})" style="font-size:11px;padding:6px 12px;background:transparent;border:1px solid rgba(74,222,128,0.3);color:var(--green);">Open ThumbGate GPT</a>
575
609
  </div>
576
610
  </div>
@@ -594,7 +628,7 @@ __GA_BOOTSTRAP__
594
628
  <div class="first-gate-steps">
595
629
  <div class="first-gate-step">
596
630
  <strong>1. Install ThumbGate</strong>
597
- <p>Run <code>npx thumbgate init</code> in your repo. Or install the <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb" style="color:var(--cyan);">Claude Extension</a>, <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" style="color:var(--cyan);">Codex plugin</a>, or <a href="/go/gpt" style="color:var(--cyan);">open the GPT</a>. Native ChatGPT rating buttons are not the ThumbGate capture path.</p>
631
+ <p>Run <code>npx thumbgate init</code> in your repo. Or install the <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb" style="color:var(--cyan);">Claude Extension</a>, <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" style="color:var(--cyan);">Codex plugin</a>, <a href="https://github.com/IgorGanapolsky/ThumbGate/tree/main/plugins/cursor-marketplace" style="color:var(--cyan);">Cursor plugin</a>, or <a href="/go/gpt" style="color:var(--cyan);">open the GPT</a>. Native ChatGPT rating buttons are not the ThumbGate capture path.</p>
598
632
  </div>
599
633
  <div class="first-gate-step">
600
634
  <strong>2. Give feedback</strong>
@@ -701,7 +735,7 @@ __GA_BOOTSTRAP__
701
735
  </div>
702
736
  <div style="display:flex;gap:12px;flex-wrap:wrap;">
703
737
  <a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb" class="btn-gpt-page" target="_blank" rel="noopener" onclick="posthog.capture('claude_section_extension_click',{cta:'install_claude_extension'})" style="background:#d97706;color:#fff;">Download Claude Extension (.mcpb)</a>
704
- <a href="/guides/claude-desktop" class="btn-free" style="display:inline-flex;align-items:center;padding:12px 20px;border-radius:8px;">Claude Desktop setup guide</a>
738
+ <a href="/guide.html" class="btn-free" style="display:inline-flex;align-items:center;padding:12px 20px;border-radius:8px;">Claude Desktop setup guide</a>
705
739
  <a href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/.claude-plugin/README.md" class="btn-free" target="_blank" rel="noopener" style="display:inline-flex;align-items:center;padding:12px 20px;border-radius:8px;">Claude plugin docs</a>
706
740
  </div>
707
741
  <p class="gpt-note"><strong>Claude Code Skill:</strong> Type <code>/thumbgate</code> in any Claude Code session. Auto-triggers on “gate”, “feedback”, “block mistake”. Free skill on top of the same local gateway.</p>
@@ -744,30 +778,40 @@ __GA_BOOTSTRAP__
744
778
  <div class="section-label">Compatibility</div>
745
779
  <h2 class="section-title">One gateway across the agent surfaces you already use</h2>
746
780
  <div class="compatibility-grid">
747
- <a class="compat-card" href="/guides/claude-desktop" style="border-color:rgba(217,119,6,0.3);background:linear-gradient(135deg, rgba(217,119,6,0.06) 0%, var(--bg-card) 100%);">
781
+ <a class="compat-card" href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb" target="_blank" rel="noopener" onclick="if(typeof posthog!=='undefined')posthog.capture('compat_claude_desktop_click',{cta:'download_claude_desktop'})" style="border-color:rgba(217,119,6,0.3);background:linear-gradient(135deg, rgba(217,119,6,0.06) 0%, var(--bg-card) 100%);">
748
782
  <h3>🧩 Claude Desktop Extension</h3>
749
- <p>Install the published Claude Desktop plugin <code>.mcpb</code> bundle today. Claude Code users can add the repo marketplace immediately with <code>/plugin marketplace add</code>. No waiting for directory approval.</p>
750
- <div class="card-arrow" style="color:#d97706;">Get the Claude plugin →</div>
783
+ <p>Install the published Claude Desktop plugin <code>.mcpb</code> bundle today. Claude Code users can add the repo marketplace immediately with <code>/plugin marketplace add</code>. No waiting for directory approval. <a href="/guide.html" style="color:var(--text-muted);text-decoration:underline;" onclick="event.stopPropagation();">60-second setup guide →</a></p>
784
+ <div class="card-arrow" style="color:#d97706;">Download .mcpb bundle →</div>
751
785
  </a>
752
- <a class="compat-card seo-card" href="https://github.com/IgorGanapolsky/ThumbGate/tree/main/.claude/skills/thumbgate" target="_blank" rel="noopener">
786
+ <a class="compat-card seo-card" href="/guides/claude-code-prevent-repeated-mistakes.html" rel="noopener">
753
787
  <h3>⚡ Claude Code Skill</h3>
754
788
  <p>Type <code>/thumbgate</code> in any Claude Code session. Auto-triggers on "gate", "feedback", "block mistake". Free skill on top of the same local gateway teams later harden into a shared workflow.</p>
755
- <div class="card-arrow">View skill on GitHub →</div>
789
+ <div class="card-arrow">Read the Claude Code guide →</div>
756
790
  </a>
757
- <a class="compat-card" href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/plugins/claude-codex-bridge/INSTALL.md" target="_blank" rel="noopener">
791
+ <a class="compat-card" href="/guide.html" rel="noopener">
758
792
  <h3>🤖 AI CLIs</h3>
759
793
  <p>Claude Code, Codex, Gemini CLI, Amp, and OpenCode all use the same gateway and memory model. Any MCP-compatible agent gets pre-action gates, feedback memory, and enforcement out of the box.</p>
760
- <div class="card-arrow">View setup guide →</div>
794
+ <div class="card-arrow">Open the setup guide →</div>
761
795
  </a>
762
- <a class="compat-card" href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/plugins/codex-profile/INSTALL.md" target="_blank" rel="noopener">
796
+ <a class="compat-card" href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-codex-plugin.zip" target="_blank" rel="noopener" onclick="if(typeof posthog!=='undefined')posthog.capture('compat_codex_plugin_click',{cta:'download_codex_plugin'})">
763
797
  <h3>🧩 Codex plugin</h3>
764
- <p>Codex ships with a published standalone ThumbGate plugin bundle plus a repo-local plugin profile. Download the zip, extract it, and install without wiring MCP by hand.</p>
765
- <div class="card-arrow">Get the Codex plugin →</div>
798
+ <p>Codex ships with a published standalone ThumbGate plugin bundle plus a repo-local plugin profile. Download the zip, extract it, and install without wiring MCP by hand. <a href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/plugins/codex-profile/INSTALL.md" target="_blank" rel="noopener" style="color:var(--text-muted);text-decoration:underline;" onclick="event.stopPropagation();">Setup instructions →</a></p>
799
+ <div class="card-arrow">Download the Codex plugin →</div>
766
800
  </a>
767
- <a class="compat-card" href="https://github.com/IgorGanapolsky/ThumbGate/tree/main/plugins" target="_blank" rel="noopener">
801
+ <a class="compat-card" href="/guides/cursor-prevent-repeated-mistakes.html" rel="noopener">
802
+ <h3>🎯 Cursor plugin</h3>
803
+ <p>Drop the ThumbGate MCP config into <code>.cursor/mcp.json</code> and Cursor gets the same pre-action gates as Claude Code and Codex. Ships with bundled rules, commands, hooks, and agents.</p>
804
+ <div class="card-arrow">Read the Cursor guide →</div>
805
+ </a>
806
+ <a class="compat-card" href="/guide.html" rel="noopener">
768
807
  <h3>✏️ Editor workflows</h3>
769
- <p>Cursor ships with a bundled marketplace plugin. VS Code works when you run an MCP-compatible agent inside it.</p>
770
- <div class="card-arrow">Browse plugins →</div>
808
+ <p>VS Code works when you run an MCP-compatible agent inside it (Continue, Cline, etc.). Any editor that speaks MCP stdio gets the same gateway.</p>
809
+ <div class="card-arrow">Open the setup guide →</div>
810
+ </a>
811
+ <a class="compat-card" href="https://mcp.so/server/thumbgate" target="_blank" rel="noopener" onclick="if(typeof posthog!=='undefined')posthog.capture('compat_mcp_so_click',{cta:'view_mcp_directory'})">
812
+ <h3>🗂️ MCP Server Directory</h3>
813
+ <p>ThumbGate is listed on <code>mcp.so</code> — the largest public MCP server directory. Install via Smithery CLI, copy-paste config for your client, or verify it's a legitimate server before running it.</p>
814
+ <div class="card-arrow">View on mcp.so →</div>
771
815
  </a>
772
816
  <a class="compat-card" href="/go/gpt?utm_source=website&utm_medium=compatibility&utm_campaign=chatgpt_gpt&cta_id=compat_open_gpt&cta_placement=compatibility" target="_blank" rel="noopener">
773
817
  <h3>💬 ChatGPT GPT Actions</h3>
@@ -872,7 +916,7 @@ __GA_BOOTSTRAP__
872
916
  <!-- HOW IT WORKS -->
873
917
  <section class="how-it-works" id="how-it-works">
874
918
  <div class="container">
875
- <div class="section-label">New in v1.5.4</div>
919
+ <div class="section-label">New in v1.5.8</div>
876
920
  <h2 class="section-title">Three steps to stop repeated AI failures</h2>
877
921
  <div class="steps">
878
922
  <div class="step">
@@ -1228,7 +1272,7 @@ __GA_BOOTSTRAP__
1228
1272
  <a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
1229
1273
  <a href="/blog">Blog</a>
1230
1274
  </div>
1231
- <span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.5.4</span>
1275
+ <span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.5.8</span>
1232
1276
  </div>
1233
1277
  </footer>
1234
1278
 
@@ -71,6 +71,7 @@ function detectAgent(flagAgent) {
71
71
  if (['codex'].includes(normalized)) return 'codex';
72
72
  if (['gemini'].includes(normalized)) return 'gemini';
73
73
  if (['forge', 'forgecode', 'forge-code'].includes(normalized)) return 'forge';
74
+ if (['cursor'].includes(normalized)) return 'cursor';
74
75
  return null;
75
76
  }
76
77
 
@@ -80,9 +81,65 @@ function detectAgent(flagAgent) {
80
81
  if (fs.existsSync(path.join(home, '.codex'))) return 'codex';
81
82
  if (fs.existsSync(path.join(home, '.gemini'))) return 'gemini';
82
83
  if (fs.existsSync(path.join(process.cwd(), 'forge.yaml'))) return 'forge';
84
+ if (fs.existsSync(path.join(process.cwd(), '.cursor'))) return 'cursor';
83
85
  return null;
84
86
  }
85
87
 
88
+ // --- Cursor wiring ---
89
+ // Cursor uses .cursor/mcp.json in the project root. We write the ThumbGate MCP
90
+ // server config there so Cursor picks up the server on next restart. Cursor's
91
+ // native hook model is different from Claude Code's — we rely on the MCP
92
+ // server's PreToolUse-equivalent enforcement via the gate-check tool.
93
+
94
+ function cursorMcpConfigPath() {
95
+ return path.join(process.cwd(), '.cursor', 'mcp.json');
96
+ }
97
+
98
+ function wireCursorHooks(options = {}) {
99
+ const mcpPath = cursorMcpConfigPath();
100
+ const dir = path.dirname(mcpPath);
101
+ const thumbgateServer = {
102
+ command: 'npx',
103
+ args: ['--yes', '--package', 'thumbgate@latest', 'thumbgate', 'serve'],
104
+ };
105
+
106
+ let existing = { mcpServers: {} };
107
+ if (fs.existsSync(mcpPath)) {
108
+ try {
109
+ existing = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
110
+ if (!existing.mcpServers) existing.mcpServers = {};
111
+ } catch {
112
+ return { changed: false, error: `Could not parse ${mcpPath}` };
113
+ }
114
+ }
115
+
116
+ const before = JSON.stringify(existing.mcpServers.thumbgate || null);
117
+ existing.mcpServers.thumbgate = thumbgateServer;
118
+ const after = JSON.stringify(existing.mcpServers.thumbgate);
119
+
120
+ const addedEntry = {
121
+ lifecycle: 'mcpServers.thumbgate',
122
+ command: `${thumbgateServer.command} ${thumbgateServer.args.join(' ')}`,
123
+ };
124
+
125
+ if (options.dryRun) {
126
+ return {
127
+ changed: before !== after,
128
+ dryRun: true,
129
+ settingsPath: mcpPath,
130
+ added: before === after ? [] : [addedEntry],
131
+ };
132
+ }
133
+
134
+ if (before === after) {
135
+ return { changed: false, settingsPath: mcpPath, added: [] };
136
+ }
137
+
138
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
139
+ fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + '\n');
140
+ return { changed: true, settingsPath: mcpPath, added: [addedEntry] };
141
+ }
142
+
86
143
  // --- Claude Code wiring ---
87
144
 
88
145
  function claudeSettingsPath() {
@@ -543,7 +600,7 @@ function wireHooks(options) {
543
600
  const agent = detectAgent(options.agent);
544
601
  if (!agent) {
545
602
  return {
546
- error: 'Could not detect AI agent. Use --agent=claude-code|codex|gemini|forge',
603
+ error: 'Could not detect AI agent. Use --agent=claude-code|codex|gemini|forge|cursor',
547
604
  agent: null,
548
605
  changed: false,
549
606
  };
@@ -563,6 +620,9 @@ function wireHooks(options) {
563
620
  case 'forge':
564
621
  result = wireForgeHooks(options);
565
622
  break;
623
+ case 'cursor':
624
+ result = wireCursorHooks(options);
625
+ break;
566
626
  default:
567
627
  return { error: `Unsupported agent: ${agent}`, agent, changed: false };
568
628
  }
@@ -971,6 +971,18 @@ function generateDashboard(feedbackDir, options = {}) {
971
971
  const feedbackTimeSeries = computeFeedbackTimeSeries(entries, 30);
972
972
  const lessonPipeline = computeLessonPipeline(feedbackDir, entries, gateStats);
973
973
 
974
+ // Estimated token savings — computed from gate blocked counts using the
975
+ // conservative methodology in scripts/token-savings.js. This is the ONLY
976
+ // place "$ saved" appears that's backed by real gate-block data; the landing
977
+ // page hero uses a hardcoded sample number disclosed as "Sample".
978
+ let tokenSavings = null;
979
+ try {
980
+ const { computeTokenSavings } = require('./token-savings');
981
+ tokenSavings = computeTokenSavings({
982
+ blockedCalls: Number(gateStats.blocked) || 0,
983
+ });
984
+ } catch { /* module missing — skip */ }
985
+
974
986
  // Merge lesson counts into feedbackTimeSeries days
975
987
  for (const day of feedbackTimeSeries.days) {
976
988
  day.lessons = lessonPipeline.lessonsByDay.get(day.dayKey) || 0;
@@ -1018,6 +1030,7 @@ function generateDashboard(feedbackDir, options = {}) {
1018
1030
  liveMetrics,
1019
1031
  predictive,
1020
1032
  feedbackTimeSeries,
1033
+ tokenSavings,
1021
1034
  lessonPipeline: {
1022
1035
  stages: lessonPipeline.stages,
1023
1036
  rates: lessonPipeline.rates,
package/src/api/server.js CHANGED
@@ -1896,7 +1896,9 @@ function renderRobotsTxt(runtimeConfig) {
1896
1896
  function renderSitemapXml(runtimeConfig) {
1897
1897
  const entries = [
1898
1898
  { path: '/', changefreq: 'weekly', priority: '1.0' },
1899
- { path: '/pro', changefreq: 'weekly', priority: '0.9' },
1899
+ // /pro consolidated into /#pro-pitch (2026-04-16) — removed from sitemap
1900
+ // so search engines don't chase the 301 instead of indexing the canonical
1901
+ // homepage directly.
1900
1902
  { path: '/llm-context.md', changefreq: 'weekly', priority: '0.8' },
1901
1903
  ...THUMBGATE_SEO_SITEMAP_ENTRIES,
1902
1904
  ];
@@ -3453,21 +3455,17 @@ async function addContext(){
3453
3455
  }
3454
3456
 
3455
3457
  if (isGetLikeRequest && pathname === '/pro') {
3456
- try {
3457
- servePublicMarketingPage({
3458
- req,
3459
- res,
3460
- parsed,
3461
- hostedConfig,
3462
- isHeadRequest,
3463
- renderHtml: loadProPageHtml,
3464
- extraTelemetry: {
3465
- pageType: 'pro_landing',
3466
- },
3467
- });
3468
- } catch (err) {
3469
- sendText(res, 500, err.message || 'Pro page unavailable');
3470
- }
3458
+ // Consolidated: /pro content now lives inline on `/` as the #pro-pitch
3459
+ // strip (hero-adjacent pricing card). 301 so external links (README,
3460
+ // plugin manifests, guides, compare pages) pass link equity onto the
3461
+ // single canonical landing page. Query string is preserved so UTM
3462
+ // tracking from inbound campaigns still reaches GA/PostHog on `/`.
3463
+ const redirectTarget = `/#pro-pitch${parsed.search || ''}`;
3464
+ res.writeHead(301, {
3465
+ Location: redirectTarget,
3466
+ 'Cache-Control': 'public, max-age=3600',
3467
+ });
3468
+ res.end();
3471
3469
  return;
3472
3470
  }
3473
3471
 
@@ -3591,7 +3589,7 @@ async function addContext(){
3591
3589
  version: pkg.version,
3592
3590
  status: 'ok',
3593
3591
  docs: 'https://github.com/IgorGanapolsky/ThumbGate',
3594
- endpoints: ['/health', '/dashboard', '/guide', '/compare', '/learn', '/pro', '/v1/feedback/capture', '/v1/feedback/stats', '/v1/feedback/summary', '/v1/lessons/search', '/v1/search', '/v1/documents', '/v1/documents/import', '/v1/documents/{documentId}', '/v1/dashboard', '/v1/dashboard/render-spec', '/v1/decisions/evaluate', '/v1/decisions/outcome', '/v1/decisions/metrics', '/v1/settings/status', '/v1/dpo/export', '/v1/jobs', '/v1/jobs/harness', '/v1/analytics/databricks/export'],
3592
+ endpoints: ['/health', '/dashboard', '/guide', '/compare', '/learn', '/v1/feedback/capture', '/v1/feedback/stats', '/v1/feedback/summary', '/v1/lessons/search', '/v1/search', '/v1/documents', '/v1/documents/import', '/v1/documents/{documentId}', '/v1/dashboard', '/v1/dashboard/render-spec', '/v1/decisions/evaluate', '/v1/decisions/outcome', '/v1/decisions/metrics', '/v1/settings/status', '/v1/dpo/export', '/v1/jobs', '/v1/jobs/harness', '/v1/analytics/databricks/export'],
3595
3593
  }, {}, {
3596
3594
  headOnly: isHeadRequest,
3597
3595
  });