thumbgate 1.27.17 → 1.27.19

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.
Files changed (107) hide show
  1. package/.claude-plugin/marketplace.json +85 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.well-known/mcp/server-card.json +1 -1
  4. package/README.md +33 -5
  5. package/adapters/chatgpt/openapi.yaml +1707 -0
  6. package/adapters/claude/.mcp.json +2 -2
  7. package/adapters/codex/config.toml +2 -2
  8. package/adapters/mcp/server-stdio.js +1 -1
  9. package/adapters/opencode/opencode.json +1 -1
  10. package/bin/cli.js +63 -54
  11. package/bin/postinstall.js +23 -14
  12. package/config/post-deploy-marketing-pages.json +19 -0
  13. package/config/tessl-tiles.json +1 -3
  14. package/package.json +99 -229
  15. package/public/guide.html +0 -4
  16. package/public/index.html +28 -49
  17. package/public/learn.html +0 -8
  18. package/public/numbers.html +2 -2
  19. package/scripts/agent-readiness.js +0 -147
  20. package/scripts/bot-detection.js +61 -3
  21. package/scripts/cli-feedback.js +4 -14
  22. package/scripts/cli-schema.js +7 -0
  23. package/scripts/durability/step.js +3 -3
  24. package/scripts/feedback-loop.js +10 -32
  25. package/scripts/gates-engine.js +72 -1
  26. package/scripts/hook-runtime.js +2 -2
  27. package/scripts/license.js +10 -9
  28. package/scripts/llm-client.js +44 -260
  29. package/scripts/mcp-config.js +7 -1
  30. package/scripts/published-cli.js +0 -8
  31. package/scripts/seo-gsd.js +0 -5
  32. package/src/api/server.js +111 -35
  33. package/.claude/commands/dashboard.md +0 -15
  34. package/.claude/commands/thumbgate-blocked.md +0 -27
  35. package/.claude/commands/thumbgate-dashboard.md +0 -15
  36. package/.claude/commands/thumbgate-doctor.md +0 -30
  37. package/.claude/commands/thumbgate-guard.md +0 -36
  38. package/.claude/commands/thumbgate-protect.md +0 -30
  39. package/.claude/commands/thumbgate-rules.md +0 -30
  40. package/adapters/gcp/dfcx-webhook-gate.js +0 -295
  41. package/adapters/letta/README.md +0 -41
  42. package/adapters/letta/thumbgate-letta-adapter.js +0 -133
  43. package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
  44. package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
  45. package/bench/observability-eval-suite.json +0 -26
  46. package/bin/dashboard-cli.js +0 -7
  47. package/commands/dashboard.md +0 -15
  48. package/commands/thumbgate-dashboard.md +0 -15
  49. package/config/builtin-lessons.json +0 -23
  50. package/public/about.html +0 -162
  51. package/public/agent-manager.html +0 -179
  52. package/public/agents-cost-savings.html +0 -153
  53. package/public/ai-malpractice-prevention.html +0 -818
  54. package/public/assets/brand/github-social-preview.png +0 -0
  55. package/public/assets/brand/thumbgate-icon-512.png +0 -0
  56. package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
  57. package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
  58. package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
  59. package/public/assets/brand/thumbgate-logo-transparent.svg +0 -22
  60. package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
  61. package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
  62. package/public/assets/brand/thumbgate-mark-team.svg +0 -26
  63. package/public/assets/brand/thumbgate-mark.svg +0 -21
  64. package/public/assets/brand/thumbgate-wordmark.svg +0 -20
  65. package/public/assets/claude-thumbgate-statusbar.svg +0 -8
  66. package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
  67. package/public/assets/legal-intake-control-flow.svg +0 -66
  68. package/public/brand/thumbgate-mark.svg +0 -19
  69. package/public/brand/thumbgate-og.svg +0 -16
  70. package/public/chatgpt-app.html +0 -330
  71. package/public/codex-enterprise.html +0 -123
  72. package/public/js/buyer-intent.js +0 -672
  73. package/public/pricing.html +0 -399
  74. package/scripts/action-receipts.js +0 -324
  75. package/scripts/activation-quickstart.js +0 -187
  76. package/scripts/agent-operations-planner.js +0 -621
  77. package/scripts/ai-component-inventory.js +0 -367
  78. package/scripts/async-eval-observability.js +0 -236
  79. package/scripts/audit.js +0 -65
  80. package/scripts/aws-blocks-guardrails.js +0 -228
  81. package/scripts/bot-detector.js +0 -50
  82. package/scripts/classifier-routing.js +0 -130
  83. package/scripts/dashboard-chat.js +0 -332
  84. package/scripts/feedback-aggregate.js +0 -281
  85. package/scripts/feedback-sanitizer.js +0 -105
  86. package/scripts/hob-pack.js +0 -591
  87. package/scripts/hook-stop-anti-claim.js +0 -301
  88. package/scripts/install-shim.js +0 -87
  89. package/scripts/mcp-oauth.js +0 -293
  90. package/scripts/noop-detect.js +0 -285
  91. package/scripts/omlx-smoke.js +0 -192
  92. package/scripts/parallel-workflow-orchestrator.js +0 -293
  93. package/scripts/plan-gate.js +0 -243
  94. package/scripts/plausible-domain-config.js +0 -99
  95. package/scripts/qa-scenario-planner.js +0 -136
  96. package/scripts/repeat-metric.js +0 -137
  97. package/scripts/secret-fixture-tokens.js +0 -61
  98. package/scripts/secret-redaction.js +0 -166
  99. package/scripts/self-harness-optimizer.js +0 -141
  100. package/scripts/self-healing-check.js +0 -193
  101. package/scripts/silent-failure-cluster.js +0 -531
  102. package/scripts/statusline-cache-read.js +0 -57
  103. package/scripts/sync-telemetry-from-prod.js +0 -374
  104. package/scripts/tool-contract-validator.js +0 -76
  105. package/scripts/trajectory-scorer.js +0 -63
  106. package/scripts/verify-marketing-pages-deployed.js +0 -195
  107. package/scripts/visitor-journey.js +0 -172
package/src/api/server.js CHANGED
@@ -20,7 +20,7 @@ const POSTHOG_STATIC_PATH_PREFIX = '/static/';
20
20
  // Stripe catalog, with the per-tier thumbnails wired in. Re-run the
21
21
  // bootstrap workflow to regenerate; the new URLs surface in the workflow
22
22
  // summary log.
23
- const FIRST_FAILURE_RULE_CHECKOUT_URL = 'https://buy.stripe.com/fZu28rfCY6zcbO99uj3sI2G';
23
+ const FIRST_FAILURE_RULE_CHECKOUT_URL = 'https://buy.stripe.com/7sY6oHaiEbTw6tP5e33sI3e';
24
24
  const QUICK_READ_CHECKOUT_URL = 'https://buy.stripe.com/5kQ7sL76s1eSaK55e33sI2H';
25
25
  const WORKFLOW_TEARDOWN_CHECKOUT_URL = 'https://buy.stripe.com/8x214n2Qc4r44lHayn3sI2I';
26
26
  const SPRINT_DIAGNOSTIC_CHECKOUT_URL = 'https://buy.stripe.com/28E00j3Uge1E2dzgWL3sI2J';
@@ -241,6 +241,8 @@ const NUMBERS_PAGE_PATH = path.resolve(__dirname, '../../public/numbers.html');
241
241
  const FEDERAL_PAGE_PATH = path.resolve(__dirname, '../../public/federal.html');
242
242
  const PRICING_PAGE_PATH = path.resolve(__dirname, '../../public/pricing.html');
243
243
  const ABOUT_PAGE_PATH = path.resolve(__dirname, '../../public/about.html');
244
+ const DIAGNOSTIC_PAGE_PATH = path.resolve(__dirname, '../../public/diagnostic.html');
245
+ const INSTALL_PAGE_PATH = path.resolve(__dirname, '../../public/install.html');
244
246
  const LEARN_DIR = path.resolve(__dirname, '../../public/learn');
245
247
  const GUIDES_DIR = path.resolve(__dirname, '../../public/guides');
246
248
  const COMPARE_DIR = path.resolve(__dirname, '../../public/compare');
@@ -2220,7 +2222,7 @@ function renderCheckoutIntentPage(prefilledEmail = '', parsed = null, options =
2220
2222
  body{background:#0a0a0a;color:#eee;font-family:system-ui,-apple-system,sans-serif;line-height:1.5}
2221
2223
  main{max-width:520px;margin:8vh auto;padding:0 20px}
2222
2224
  .brand{display:flex;align-items:center;gap:10px;margin-bottom:24px;font-size:14px;color:#94a3b8}
2223
- .brand-mark{width:36px;height:36px;display:block;flex:0 0 auto;border-radius:9px}
2225
+ .brand-mark{width:24px;height:24px;background:#22d3ee;border-radius:6px;display:inline-block}
2224
2226
  h1{font-size:24px;margin:0 0 8px;color:#fff}.price{font-size:32px;font-weight:700;color:#22d3ee;margin:8px 0 4px}.price small{font-size:14px;color:#94a3b8;font-weight:400}
2225
2227
  p{color:#cbd5e1;margin:8px 0}form{margin:0}
2226
2228
  input[type=email]{width:100%;box-sizing:border-box;padding:14px 16px;border:1px solid #374151;border-radius:8px;background:#111827;color:#fff;font-size:15px;margin:16px 0 0;outline:none}
@@ -2239,14 +2241,13 @@ a{display:block;text-decoration:none}a.secondary{border:1px solid #374151;color:
2239
2241
  </head>
2240
2242
  <body>
2241
2243
  <main>
2242
- <div class="brand"><img src="/thumbgate-icon.png?v=20260623-stripe-match" alt="" class="brand-mark" width="36" height="36"><span>ThumbGate</span></div>
2244
+ <div class="brand"><span class="brand-mark"></span><span>ThumbGate</span></div>
2243
2245
  <h1>Start ThumbGate Pro</h1>
2244
2246
  <div class="price">$19<small>/mo</small></div>
2245
2247
  <p>The npm package runs your gates locally. <strong>Pro</strong> is what keeps them working across every machine, every agent runtime, and every breaking-change week.</p>
2246
- <form action="/checkout/pro" method="GET" data-i="pro_checkout_confirmed">
2248
+ <form action="https://buy.stripe.com/8x2dR91M84r4cSd9uj3sI3f" method="GET" data-i="pro_checkout_confirmed">
2247
2249
  ${hiddenInputs}
2248
- <input type="hidden" name="confirm" value="1">
2249
- <input type="email" name="customer_email" value="${escapeHtmlAttribute(prefilledEmail)}" placeholder="you@company.com" autocomplete="email">
2250
+ <input type="email" name="prefilled_email" value="${escapeHtmlAttribute(prefilledEmail)}" placeholder="you@company.com" autocomplete="email">
2250
2251
  <p class="email-note">Optional. Stripe can collect your email on the secure checkout page.</p>
2251
2252
  <button type="submit" class="primary">Pay $19/mo with Stripe →</button>
2252
2253
  </form>
@@ -2862,8 +2863,8 @@ function loadPublicMarketingTemplateHtml(templatePath, runtimeConfig, pageContex
2862
2863
  '__SERVER_ACQUISITION_ID__': pageContext.serverAcquisitionId || '',
2863
2864
  '__SERVER_TELEMETRY_CAPTURED__': pageContext.serverTelemetryCaptured ? 'true' : 'false',
2864
2865
  '__VERIFICATION_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
2865
- '__COMPATIBILITY_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/proof/compatibility/report.json',
2866
- '__AUTOMATION_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/proof/automation/report.json',
2866
+ '__COMPATIBILITY_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
2867
+ '__AUTOMATION_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
2867
2868
  '__GTM_PLAN_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/GO_TO_MARKET_REVENUE_WEDGE_2026-03.md',
2868
2869
  '__GITHUB_URL__': 'https://github.com/IgorGanapolsky/ThumbGate',
2869
2870
  '__POSTHOG_API_KEY__': runtimeConfig.posthogApiKey || '',
@@ -2878,6 +2879,14 @@ function loadProPageHtml(runtimeConfig, pageContext = {}) {
2878
2879
  return loadPublicMarketingTemplateHtml(PRO_PAGE_PATH, runtimeConfig, pageContext);
2879
2880
  }
2880
2881
 
2882
+ function loadDiagnosticPageHtml(runtimeConfig, pageContext = {}) {
2883
+ return loadPublicMarketingTemplateHtml(DIAGNOSTIC_PAGE_PATH, runtimeConfig, pageContext);
2884
+ }
2885
+
2886
+ function loadInstallPageHtml(runtimeConfig, pageContext = {}) {
2887
+ return loadPublicMarketingTemplateHtml(INSTALL_PAGE_PATH, runtimeConfig, pageContext);
2888
+ }
2889
+
2881
2890
  function loadPricingPageHtml(runtimeConfig, pageContext = {}) {
2882
2891
  return loadPublicMarketingTemplateHtml(PRICING_PAGE_PATH, runtimeConfig, pageContext);
2883
2892
  }
@@ -3511,6 +3520,9 @@ function renderSitemapXml(runtimeConfig) {
3511
3520
  const entries = [
3512
3521
  { path: '/', changefreq: 'weekly', priority: '1.0' },
3513
3522
  { path: '/pro', changefreq: 'weekly', priority: '0.9' },
3523
+ { path: '/diagnostic', changefreq: 'weekly', priority: '0.9' },
3524
+ { path: '/workflow-hardening-sprint', changefreq: 'weekly', priority: '0.9' },
3525
+ { path: '/install', changefreq: 'weekly', priority: '0.9' },
3514
3526
  { path: '/agent-manager', changefreq: 'weekly', priority: '0.9' },
3515
3527
  { path: '/llm-context.md', changefreq: 'weekly', priority: '0.8' },
3516
3528
  { path: '/chatgpt-app', changefreq: 'weekly', priority: '0.85' },
@@ -5209,7 +5221,7 @@ function createApiServer() {
5209
5221
  if (req.method === 'POST' && pathname === '/api/event') {
5210
5222
  // Filter bots from analytics to keep Plausible data clean
5211
5223
  let _botDetector;
5212
- try { _botDetector = require('../../scripts/bot-detector'); } catch (_e) { _botDetector = null; }
5224
+ try { _botDetector = require('../../scripts/bot-detection'); } catch (_e) { _botDetector = null; }
5213
5225
  if (_botDetector && _botDetector.shouldExcludeFromAnalytics(req)) {
5214
5226
  sendJson(res, 202, { status: 'filtered', reason: 'bot' });
5215
5227
  return;
@@ -5614,6 +5626,32 @@ async function addContext(){
5614
5626
  return;
5615
5627
  }
5616
5628
 
5629
+ if (isGetLikeRequest && (
5630
+ pathname === '/install'
5631
+ || pathname === '/install.html'
5632
+ || pathname === '/marketplace'
5633
+ || pathname === '/marketplace.html'
5634
+ || pathname === '/marketplaces'
5635
+ || pathname === '/marketplaces.html'
5636
+ )) {
5637
+ try {
5638
+ servePublicMarketingPage({
5639
+ req,
5640
+ res,
5641
+ parsed,
5642
+ hostedConfig,
5643
+ isHeadRequest,
5644
+ renderHtml: loadInstallPageHtml,
5645
+ extraTelemetry: {
5646
+ pageType: 'install',
5647
+ },
5648
+ });
5649
+ } catch (err) {
5650
+ sendText(res, 500, err.message || 'Install page unavailable');
5651
+ }
5652
+ return;
5653
+ }
5654
+
5617
5655
  if (isGetLikeRequest && (pathname === '/codex-plugin' || pathname === '/codex-plugin.html')) {
5618
5656
  try {
5619
5657
  const html = fs.readFileSync(CODEX_PLUGIN_PAGE_PATH, 'utf-8');
@@ -5704,13 +5742,16 @@ async function addContext(){
5704
5742
  return;
5705
5743
  }
5706
5744
 
5707
- // Natural marketing URLs for the Workflow Hardening Sprint. Outbound
5708
- // messages, social posts, and word-of-mouth all refer to "the sprint"
5709
- // or "workflow hardening" recipients who type or paste the natural
5710
- // URLs currently hit the generic API 401 JSON error and bounce.
5711
- // Redirect them to the canonical intake anchor instead.
5745
+ // Natural marketing URLs for the Workflow Hardening Diagnostic/Sprint.
5746
+ // High-intent outreach refers to "the diagnostic", "the sprint", or
5747
+ // "workflow hardening"; serve a focused buyer page instead of sending
5748
+ // qualified traffic to the generic homepage anchor.
5712
5749
  if (isGetLikeRequest && (
5713
- pathname === '/sprint'
5750
+ pathname === '/diagnostic'
5751
+ || pathname === '/diagnostic.html'
5752
+ || pathname === '/workflow-diagnostic'
5753
+ || pathname === '/workflow-diagnostic.html'
5754
+ || pathname === '/sprint'
5714
5755
  || pathname === '/sprint.html'
5715
5756
  || pathname === '/workflow-hardening'
5716
5757
  || pathname === '/workflow-hardening.html'
@@ -5719,18 +5760,28 @@ async function addContext(){
5719
5760
  || pathname === '/workflow-sprint'
5720
5761
  || pathname === '/workflow-sprint.html'
5721
5762
  )) {
5722
- res.writeHead(302, {
5723
- Location: '/#workflow-sprint-intake',
5724
- 'Cache-Control': 'no-store',
5725
- });
5726
- res.end();
5763
+ try {
5764
+ servePublicMarketingPage({
5765
+ req,
5766
+ res,
5767
+ parsed,
5768
+ hostedConfig,
5769
+ isHeadRequest,
5770
+ renderHtml: loadDiagnosticPageHtml,
5771
+ extraTelemetry: {
5772
+ pageType: 'diagnostic',
5773
+ },
5774
+ });
5775
+ } catch (err) {
5776
+ sendText(res, 500, err.message || 'Diagnostic page unavailable');
5777
+ }
5727
5778
  return;
5728
5779
  }
5729
5780
 
5730
5781
  if (isGetLikeRequest && (pathname === '/numbers' || pathname === '/numbers.html')) {
5731
5782
  // Route through servePublicMarketingPage so landing_page_view telemetry
5732
5783
  // + funnel-events.jsonl `discovery/landing_view` get captured with UTM
5733
- // attribution — critical for DirectSocial social CTAs that target /numbers.
5784
+ // attribution — critical for Zernio social CTAs that target /numbers.
5734
5785
  try {
5735
5786
  servePublicMarketingPage({
5736
5787
  req,
@@ -6021,16 +6072,20 @@ async function addContext(){
6021
6072
  // (form submission JS-less bots don't do), (b) a `customer_email`
6022
6073
  // query param treats the request as confirmed even from a bot UA,
6023
6074
  // because no real crawler appends customer_email to discovered URLs.
6075
+ const rawCheckoutEmail = normalizeNullableText(bootstrapBody.customerEmail);
6076
+ const normalizedCheckoutEmail = normalizeCheckoutCustomerEmail(rawCheckoutEmail);
6024
6077
  const hasCustomerEmailHint = !!parsed?.searchParams?.has('customer_email');
6025
- const botShouldBypass = !botClassification.isBot || hasCustomerEmailHint;
6026
- // 2026-06-04 audit: after the POST-401 fix, 3 of 4 fresh /checkout/pro
6027
- // sessions had customer_email=null in Stripe (zombie sessions with no
6028
- // recovery surface). Root cause: POSTs were auto-confirmed regardless of
6029
- // whether the email query param was present. Require email on POSTs too
6030
- // so emails-less POSTs fall through to the interstitial form instead of
6031
- // creating an un-recoverable Stripe session.
6032
- const isConfirmedCheckout = (req.method === 'POST' && hasCustomerEmailHint)
6033
- || (hasConfirmFlag && botShouldBypass);
6078
+ const hasValidCustomerEmailHint = !!normalizedCheckoutEmail;
6079
+ const botShouldBypass = !botClassification.isBot || hasValidCustomerEmailHint;
6080
+ // 2026-06-29 conversion audit: requiring email before Stripe gave us a
6081
+ // recoverable abandoned-session theory, but the hosted funnel showed
6082
+ // traffic with 0 checkout starts. Let confirmed human clicks reach
6083
+ // Stripe; Stripe can collect email on the secure checkout page. Bots
6084
+ // still need a valid email hint to bypass deflection.
6085
+ const isConfirmedCheckout = (
6086
+ (req.method === 'POST' && hasValidCustomerEmailHint)
6087
+ || hasConfirmFlag
6088
+ ) && botShouldBypass;
6034
6089
  // 2026-06-05 revenue bypass: env-gated direct-to-Stripe redirect.
6035
6090
  // Live 30d billing showed 254 interstitial views → 1 Stripe click-through
6036
6091
  // → 0 paid. When THUMBGATE_CHECKOUT_INTERSTITIAL_BYPASS=1 is set we
@@ -6038,7 +6093,7 @@ async function addContext(){
6038
6093
  // pro Stripe Payment Link, preserving UTM + attribution metadata via
6039
6094
  // buildCheckoutFallbackUrl. Default-off; bot-deflection still applies
6040
6095
  // (bot + no email hint still falls through to the existing interstitial).
6041
- const interstitialBypassEnabled = process.env.THUMBGATE_CHECKOUT_INTERSTITIAL_BYPASS === '1';
6096
+ const interstitialBypassEnabled = process.env.THUMBGATE_CHECKOUT_INTERSTITIAL_BYPASS !== '0';
6042
6097
  const interstitialSampleRate = normalizeCheckoutInterstitialSampleRate(
6043
6098
  process.env.THUMBGATE_CHECKOUT_INTERSTITIAL_SAMPLE_RATE
6044
6099
  );
@@ -6124,6 +6179,7 @@ async function addContext(){
6124
6179
  const eventType = botClassification.isBot
6125
6180
  ? 'checkout_bot_deflected'
6126
6181
  : 'checkout_interstitial_view';
6182
+ const missingConfirmedEmail = hasConfirmFlag && !hasValidCustomerEmailHint;
6127
6183
  appendBestEffortTelemetry(FEEDBACK_DIR, {
6128
6184
  eventType,
6129
6185
  clientType: 'web',
@@ -6147,7 +6203,10 @@ async function addContext(){
6147
6203
  isBot: botClassification.isBot ? 'true' : 'false',
6148
6204
  interstitialSampled: interstitialSampled ? 'true' : 'false',
6149
6205
  interstitialSampleRate,
6150
- reason: botClassification.reason,
6206
+ reason: missingConfirmedEmail
6207
+ ? (hasCustomerEmailHint ? 'invalid_customer_email' : 'missing_customer_email')
6208
+ : botClassification.reason,
6209
+ confirmEmailRequired: missingConfirmedEmail ? 'true' : 'false',
6151
6210
  }, req.headers, eventType);
6152
6211
  const prefilledEmail = parsed?.searchParams?.get('customer_email') || '';
6153
6212
  const html = renderCheckoutIntentPage(prefilledEmail, parsed, {
@@ -6157,8 +6216,6 @@ async function addContext(){
6157
6216
  return;
6158
6217
  }
6159
6218
 
6160
- const rawCheckoutEmail = normalizeNullableText(bootstrapBody.customerEmail);
6161
- const normalizedCheckoutEmail = normalizeCheckoutCustomerEmail(rawCheckoutEmail);
6162
6219
  if (!normalizedCheckoutEmail) {
6163
6220
  appendBestEffortTelemetry(FEEDBACK_DIR, {
6164
6221
  eventType: 'checkout_email_deferred_to_stripe',
@@ -6434,6 +6491,25 @@ async function addContext(){
6434
6491
  return;
6435
6492
  }
6436
6493
 
6494
+ if (isGetLikeRequest && pathname === '/leash-beta') {
6495
+ // Hermes Mobile founding beta landing (mac-yolo-safeguards/hermes-mobile/docs/beta-page).
6496
+ try {
6497
+ const html = fs.readFileSync(
6498
+ path.resolve(__dirname, '../../assets/static/leash-beta.html'),
6499
+ 'utf8'
6500
+ );
6501
+ if (isHeadRequest) {
6502
+ sendHtml(res, 200, html, {}, { headOnly: true });
6503
+ return;
6504
+ }
6505
+ sendHtml(res, 200, html);
6506
+ } catch (err) {
6507
+ console.error('leash-beta page read failed:', err?.message);
6508
+ sendJson(res, 500, { error: 'leash-beta page unavailable' });
6509
+ }
6510
+ return;
6511
+ }
6512
+
6437
6513
  if (isGetLikeRequest && pathname === '/.well-known/mcp.json') {
6438
6514
  sendJson(res, 200, getMcpDiscoveryManifest(hostedConfig), {}, {
6439
6515
  headOnly: isHeadRequest,
@@ -6809,7 +6885,7 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
6809
6885
  }
6810
6886
 
6811
6887
  if (req.method === 'GET' && pathname === '/v1/metrics/real') {
6812
- const bd = require('../../scripts/bot-detector');
6888
+ const bd = require('../../scripts/bot-detection');
6813
6889
  const { FEEDBACK_DIR: metricsDir } = getFeedbackPaths();
6814
6890
  const telemetryPath = path.join(metricsDir, 'telemetry-pings.jsonl');
6815
6891
 
@@ -1,15 +0,0 @@
1
- ---
2
- name: dashboard
3
- description: Open the local HTTP dashboard for the current project in your web browser.
4
- ---
5
-
6
- # Open Dashboard
7
-
8
- Open the local HTTP dashboard for the current project in your web browser.
9
-
10
- ## Instructions
11
- Execute the following command in the project directory to open the browser dashboard scoped to the current repository:
12
- ```bash
13
- thumbgate-dashboard
14
- ```
15
-
@@ -1,27 +0,0 @@
1
- ---
2
- name: thumbgate-blocked
3
- description: Show what ThumbGate has actually blocked — gate enforcement stats and the full enforcement matrix. Use for "what has ThumbGate blocked", "show gate stats", "is enforcement working", "how many tokens did we save", "enforcement matrix".
4
- allowed-tools: mcp__thumbgate__gate_stats, mcp__thumbgate__enforcement_matrix, Bash(npx thumbgate gate-stats:*)
5
- ---
6
-
7
- # ThumbGate Blocked
8
-
9
- Show the enforcement record: how many risky actions were blocked vs warned, which gates fire most, and the full feedback → check → rejection pipeline.
10
-
11
- This command wraps existing ThumbGate capability — **no new logic**. It reads the live enforcement counters.
12
-
13
- ## Steps
14
-
15
- 1. Call the `gate_stats` MCP tool for the headline numbers: blocked count, warned count, and the top gates by hits. (CLI fallback: `npx thumbgate gate-stats`.)
16
- 2. Call the `enforcement_matrix` MCP tool for the full picture: feedback pipeline stats, active pre-action checks, and the rejection ledger with revival conditions.
17
- 3. Summarize for the user:
18
- - Total blocks (each block = a repeat mistake stopped before it spent tokens or did damage).
19
- - Most-triggered gates.
20
- - Anything in the rejection ledger that is close to revival.
21
- 4. If counts are all zero, note that enforcement is wired but hasn't fired yet, and point to `/thumbgate-guard` to promote a rule.
22
-
23
- ## Example
24
-
25
- ```
26
- /thumbgate-blocked
27
- ```
@@ -1,15 +0,0 @@
1
- ---
2
- name: thumbgate-dashboard
3
- description: Open the local HTTP dashboard for the current project in your web browser.
4
- ---
5
-
6
- # Open Scoped ThumbGate Dashboard
7
-
8
- Open the local HTTP dashboard for the current project in your web browser.
9
-
10
- ## Instructions
11
- Execute the following command in the project directory to open the browser dashboard:
12
- ```bash
13
- thumbgate-dashboard
14
- ```
15
-
@@ -1,30 +0,0 @@
1
- ---
2
- name: thumbgate-doctor
3
- description: Health-check the ThumbGate wiring for this project — hooks, MCP server, and agent-readiness — and report what's broken. Use for "is ThumbGate wired up", "thumbgate doctor", "check my guardrails are installed", "why aren't my gates firing", "agent readiness".
4
- allowed-tools: Bash(npx thumbgate doctor:*), mcp__thumbgate__check_operational_integrity
5
- ---
6
-
7
- # ThumbGate Doctor
8
-
9
- Audit whether ThumbGate is actually wired into this agent: PreToolUse / SessionStart hooks installed, MCP server reachable, lesson store present, and overall agent-readiness — then tell the user exactly what to fix.
10
-
11
- This command wraps existing ThumbGate capability — **no new logic**. It runs the existing doctor + integrity checks.
12
-
13
- ## Steps
14
-
15
- 1. Run the existing wiring/health audit:
16
- ```bash
17
- npx thumbgate doctor
18
- ```
19
- (Add `--json` for a machine-readable report.) It exits non-zero when the project is not `ready`.
20
- 2. For deeper runtime state, call the `check_operational_integrity` MCP tool to verify the server-side enforcement path is live, not just the local config.
21
- 3. Summarize:
22
- - ✅ what's wired (hooks, MCP, store, statusline).
23
- - ❌ what's missing, with the exact fix command (usually `npx thumbgate init`).
24
- 4. If everything is green, say so plainly with the readiness status; if not, lead with the single highest-impact fix.
25
-
26
- ## Example
27
-
28
- ```
29
- /thumbgate-doctor
30
- ```
@@ -1,36 +0,0 @@
1
- ---
2
- name: thumbgate-guard
3
- description: Turn the last agent mistake into a hard prevention rule the agent cannot bypass. Use after a bad tool call, a wrong action, or a thumbs-down — "guard against this", "block this from happening again", "never do that again", "promote this to a rule".
4
- allowed-tools: mcp__thumbgate__capture_feedback, Bash(npx thumbgate force-gate:*), Bash(npx thumbgate quickstart:*)
5
- ---
6
-
7
- # ThumbGate Guard
8
-
9
- Capture the mistake the agent just made and promote it into a Pre-Action Check (a `block` gate) so the same tool-call shape is intercepted before it runs again — in this and every future session, across Claude Code, Cursor, Codex, Gemini, Amp, and Cline.
10
-
11
- This command wraps existing ThumbGate capability. It adds **no new logic** — it routes to the real capture + force-promote path.
12
-
13
- ## Steps
14
-
15
- 1. Identify the specific bad action from the recent conversation (e.g. `git push --force origin main`, `DROP TABLE users`, deploy without tests). State it in one sentence.
16
- 2. Record the signal with the `capture_feedback` MCP tool:
17
- - `signal: "down"`
18
- - `context`: one sentence describing what went wrong
19
- - `whatWentWrong`: the concrete failure
20
- - `whatToChange`: the prevention action
21
- - `tags`: the domain (e.g. `git`, `database`, `deploy`)
22
- - If the user only gave a vague signal, pass the recent turns through `conversationWindow` / `chatHistory` for history-aware distillation instead of refusing.
23
- 3. Promote it to an enforced block gate using the existing force-promote path:
24
- ```bash
25
- npx thumbgate force-gate "<one-sentence context of the mistake>"
26
- ```
27
- This prints the new `gateId` and the total active gate count.
28
- 4. Show the user the promoted rule and confirm it is now enforced as a PreToolUse block.
29
-
30
- > First rule of the project and want the guided walkthrough (capture → promote → watch it block once)? Run `npx thumbgate quickstart` instead.
31
-
32
- ## Example
33
-
34
- ```
35
- /thumbgate-guard the agent force-pushed to main and overwrote a teammate's commit
36
- ```
@@ -1,30 +0,0 @@
1
- ---
2
- name: thumbgate-protect
3
- description: Show this repo's branch/release governance and grant a scoped, time-limited approval for an action that touches protected files. Use for "protect this branch", "is main protected", "approve this protected change", "branch governance", "let me edit a protected file".
4
- allowed-tools: mcp__thumbgate__get_branch_governance, mcp__thumbgate__approve_protected_action
5
- ---
6
-
7
- # ThumbGate Protect
8
-
9
- Inspect the protected-action posture for this project and, when the user explicitly approves, grant a scoped, expiring exception so a protected-file edit or publish can proceed under audit.
10
-
11
- This command wraps existing ThumbGate capability — **no new logic**. It reads governance state and records a time-boxed approval.
12
-
13
- ## Steps
14
-
15
- 1. Read the current posture with the `get_branch_governance` MCP tool: which branches are protected, release rules, and the protected-file globs in effect.
16
- 2. Report it plainly: what is protected, and what the agent is currently blocked from touching without approval.
17
- 3. **Only if the user explicitly asks to proceed**, grant a scoped approval with `approve_protected_action`:
18
- - `pathGlobs`: the smallest set of protected globs the action needs.
19
- - `reason`: why this is approved (one sentence).
20
- - `evidence`: supporting note (tests passing, owner sign-off, etc.) when available.
21
- - `ttlMs`: keep it short — default is 1 hour, never exceed what the task needs.
22
- 4. Confirm the approval id, covered globs, and expiry. Approvals are deliberately temporary and audited; re-run for the next task.
23
-
24
- > This is for granting *narrow, temporary* exceptions, not for disabling protection. Never use it to bypass branch governance wholesale.
25
-
26
- ## Example
27
-
28
- ```
29
- /thumbgate-protect
30
- ```
@@ -1,30 +0,0 @@
1
- ---
2
- name: thumbgate-rules
3
- description: List the active prevention rules and learned lessons guarding this project. Use to answer "what is ThumbGate protecting me from", "show my gates/rules", "what has the agent learned", "what's blocked here".
4
- allowed-tools: mcp__thumbgate__prevention_rules, mcp__thumbgate__get_reliability_rules, mcp__thumbgate__search_lessons, Bash(npx thumbgate rules:*)
5
- ---
6
-
7
- # ThumbGate Rules
8
-
9
- Show the guardrails currently in force for this project: the auto-promoted prevention rules, the reliability rules, and the promoted lessons behind them.
10
-
11
- This command wraps existing ThumbGate capability — **no new logic**. It reads the live rule + lesson stores.
12
-
13
- ## Steps
14
-
15
- 1. List the active prevention rules with the `prevention_rules` MCP tool (or the CLI fallback `npx thumbgate rules`).
16
- 2. Pull the reliability rules with `get_reliability_rules` to show which tool-call shapes are gated.
17
- 3. For each rule, surface the lesson it came from with `search_lessons` so the user sees *why* the rule exists, not just *what* it blocks.
18
- 4. Present a compact table:
19
-
20
- | Rule / Gate | Blocks | From lesson | State |
21
- |-------------|--------|-------------|-------|
22
- | … | … | … | active / archived |
23
-
24
- 5. If there are zero active rules, point the user to `/thumbgate-guard` to promote their first one.
25
-
26
- ## Example
27
-
28
- ```
29
- /thumbgate-rules
30
- ```