thumbgate 0.9.9 → 0.9.11

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 (160) hide show
  1. package/.claude-plugin/README.md +4 -4
  2. package/.claude-plugin/marketplace.json +4 -2
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.well-known/mcp/server-card.json +1 -1
  5. package/README.md +115 -312
  6. package/adapters/README.md +2 -2
  7. package/adapters/amp/skills/{rlhf-feedback → thumbgate-feedback}/SKILL.md +1 -1
  8. package/adapters/chatgpt/openapi.yaml +2 -2
  9. package/adapters/claude/.mcp.json +3 -3
  10. package/adapters/codex/config.toml +4 -4
  11. package/adapters/gemini/function-declarations.json +1 -1
  12. package/adapters/mcp/server-stdio.js +66 -6
  13. package/adapters/opencode/opencode.json +4 -2
  14. package/bin/cli.js +188 -39
  15. package/config/e2e-critical-flows.json +4 -0
  16. package/config/gates/default.json +74 -2
  17. package/config/github-about.json +1 -1
  18. package/config/mcp-allowlists.json +33 -6
  19. package/config/skill-packs/react-testing.json +1 -1
  20. package/config/tessl-tiles.json +3 -3
  21. package/openapi/openapi.yaml +2 -2
  22. package/package.json +23 -9
  23. package/plugins/amp-skill/INSTALL.md +3 -2
  24. package/plugins/amp-skill/SKILL.md +1 -0
  25. package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
  26. package/plugins/claude-codex-bridge/.mcp.json +5 -3
  27. package/plugins/claude-codex-bridge/README.md +1 -1
  28. package/plugins/claude-codex-bridge/skills/setup/SKILL.md +1 -1
  29. package/plugins/claude-skill/INSTALL.md +4 -3
  30. package/plugins/claude-skill/SKILL.md +1 -1
  31. package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
  32. package/plugins/codex-profile/.mcp.json +5 -3
  33. package/plugins/codex-profile/INSTALL.md +2 -2
  34. package/plugins/codex-profile/README.md +1 -1
  35. package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
  36. package/plugins/cursor-marketplace/README.md +5 -5
  37. package/plugins/cursor-marketplace/mcp.json +4 -2
  38. package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +1 -1
  39. package/plugins/cursor-marketplace/scripts/gate-check.sh +15 -5
  40. package/plugins/gemini-extension/INSTALL.md +4 -4
  41. package/plugins/opencode-profile/INSTALL.md +5 -5
  42. package/public/dashboard.html +15 -8
  43. package/public/index.html +134 -375
  44. package/public/js/buyer-intent.js +252 -0
  45. package/public/pro.html +1085 -0
  46. package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
  47. package/scripts/adk-consolidator.js +17 -5
  48. package/scripts/agent-readiness.js +3 -1
  49. package/scripts/agent-security-hardening.js +4 -4
  50. package/scripts/auto-promote-gates.js +8 -0
  51. package/scripts/auto-wire-hooks.js +105 -21
  52. package/scripts/billing.js +111 -7
  53. package/scripts/build-metadata.js +14 -0
  54. package/scripts/check-congruence.js +1 -1
  55. package/scripts/context-engine.js +2 -1
  56. package/scripts/daemon-manager.js +2 -2
  57. package/scripts/dashboard.js +2 -2
  58. package/scripts/data-governance.js +1 -1
  59. package/scripts/deploy-gcp.sh +1 -1
  60. package/scripts/deploy-policy.js +22 -4
  61. package/scripts/dispatch-brief.js +1 -1
  62. package/scripts/ensure-repo-bootstrap.js +1 -1
  63. package/scripts/feedback-attribution.js +22 -10
  64. package/scripts/feedback-fallback.js +3 -2
  65. package/scripts/feedback-inbox-read.js +1 -1
  66. package/scripts/feedback-loop.js +41 -3
  67. package/scripts/feedback-paths.js +8 -8
  68. package/scripts/feedback-schema.js +1 -1
  69. package/scripts/feedback-to-memory.js +2 -2
  70. package/scripts/filesystem-search.js +2 -2
  71. package/scripts/gates-engine.js +765 -34
  72. package/scripts/generate-paperbanana-diagrams.sh +3 -3
  73. package/scripts/github-about.js +1 -1
  74. package/scripts/gtm-revenue-loop.js +20 -1
  75. package/scripts/hook-runtime.js +89 -0
  76. package/scripts/hook-stop-self-score.sh +3 -3
  77. package/scripts/hook-thumbgate-cache-updater.js +98 -37
  78. package/scripts/hosted-config.js +12 -10
  79. package/scripts/hybrid-feedback-context.js +54 -13
  80. package/scripts/install-mcp.js +14 -1
  81. package/scripts/intent-router.js +1 -1
  82. package/scripts/internal-agent-bootstrap.js +1 -1
  83. package/scripts/lesson-inference.js +6 -1
  84. package/scripts/license.js +54 -16
  85. package/scripts/mcp-config.js +69 -7
  86. package/scripts/memory-migration.js +1 -1
  87. package/scripts/money-watcher.js +166 -16
  88. package/scripts/operational-integrity.js +480 -0
  89. package/scripts/optimize-context.js +1 -1
  90. package/scripts/perplexity-marketing.js +1 -1
  91. package/scripts/post-everywhere.js +7 -12
  92. package/scripts/post-to-x.js +1 -1
  93. package/scripts/pr-manager.js +14 -11
  94. package/scripts/problem-detail.js +10 -10
  95. package/scripts/profile-router.js +2 -0
  96. package/scripts/prompt-dlp.js +1 -0
  97. package/scripts/prove-adapters.js +6 -6
  98. package/scripts/prove-automation.js +1 -1
  99. package/scripts/prove-autoresearch.js +1 -1
  100. package/scripts/prove-claim-verification.js +3 -3
  101. package/scripts/prove-data-pipeline.js +5 -5
  102. package/scripts/prove-data-quality.js +1 -1
  103. package/scripts/prove-evolution.js +7 -7
  104. package/scripts/prove-harnesses.js +2 -2
  105. package/scripts/prove-lancedb.js +2 -2
  106. package/scripts/prove-local-intelligence.js +1 -1
  107. package/scripts/prove-loop-closure.js +1 -1
  108. package/scripts/prove-predictive-insights.js +2 -2
  109. package/scripts/prove-runtime.js +6 -6
  110. package/scripts/prove-seo-gsd.js +1 -1
  111. package/scripts/prove-settings.js +4 -4
  112. package/scripts/prove-subway-upgrades.js +1 -1
  113. package/scripts/prove-tessl.js +2 -2
  114. package/scripts/prove-xmemory.js +2 -2
  115. package/scripts/publish-decision.js +10 -0
  116. package/scripts/published-cli.js +34 -0
  117. package/scripts/rate-limiter.js +2 -2
  118. package/scripts/reddit-monitor-cron.sh +2 -2
  119. package/scripts/reminder-engine.js +1 -1
  120. package/scripts/schedule-manager.js +3 -3
  121. package/scripts/self-healing-check.js +1 -1
  122. package/scripts/shieldcortex-memory-firewall-runner.mjs +1 -1
  123. package/scripts/skill-quality-tracker.js +1 -1
  124. package/scripts/social-analytics/db/social-analytics.db-shm +0 -0
  125. package/scripts/social-analytics/db/social-analytics.db-wal +0 -0
  126. package/scripts/social-analytics/engagement-audit.js +202 -0
  127. package/scripts/social-analytics/generate-instagram-card.js +1 -1
  128. package/scripts/social-analytics/instagram-thumbgate-post.js +5 -1
  129. package/scripts/social-analytics/install-growth-automation.js +114 -0
  130. package/scripts/social-analytics/publish-instagram-thumbgate.js +8 -2
  131. package/scripts/social-analytics/publish-thumbgate-launch.js +1 -1
  132. package/scripts/social-analytics/publishers/reddit.js +7 -12
  133. package/scripts/social-analytics/publishers/zernio.js +19 -0
  134. package/scripts/social-analytics/reconcile-thumbgate-campaign.js +165 -0
  135. package/scripts/social-analytics/schedule-thumbgate-campaign.js +275 -0
  136. package/scripts/social-analytics/sync-launch-assets.js +185 -0
  137. package/scripts/social-pipeline.js +2 -2
  138. package/scripts/social-post-hourly.js +185 -0
  139. package/scripts/social-quality-gate.js +119 -3
  140. package/scripts/social-reply-monitor.js +150 -34
  141. package/scripts/statusline-cache-path.js +27 -0
  142. package/scripts/statusline-meta.js +22 -0
  143. package/scripts/statusline.sh +24 -32
  144. package/scripts/sync-version.js +24 -12
  145. package/scripts/telemetry-analytics.js +4 -4
  146. package/scripts/tessl-export.js +1 -1
  147. package/scripts/test-coverage.js +20 -13
  148. package/scripts/thumbgate-search.js +2 -2
  149. package/scripts/tool-registry.js +98 -1
  150. package/scripts/train_from_feedback.py +1 -1
  151. package/scripts/user-profile.js +4 -4
  152. package/scripts/validate-feedback.js +1 -1
  153. package/scripts/vector-store.js +1 -1
  154. package/scripts/verification-loop.js +1 -1
  155. package/scripts/verify-run.js +1 -1
  156. package/scripts/weekly-auto-post.js +1 -1
  157. package/skills/{rlhf-feedback → thumbgate-feedback}/SKILL.md +1 -1
  158. package/src/api/server.js +291 -41
  159. package/scripts/__pycache__/train_from_feedback.cpython-314.pyc +0 -0
  160. package/scripts/social-analytics/db/social-analytics.db +0 -0
@@ -1,9 +1,9 @@
1
1
  # Codex MCP profile (copy into ~/.codex/config.toml or merge section)
2
2
  [mcp_servers.thumbgate]
3
- command = "node"
4
- args = ["bin/cli.js", "serve"]
3
+ command = "npx"
4
+ args = ["--yes", "--package", "thumbgate@0.9.11", "thumbgate", "serve"]
5
5
 
6
6
  # Hard PreToolUse hook for Codex
7
7
  [hooks.pre_tool_use]
8
- command = "node"
9
- args = ["bin/cli.js", "gate"]
8
+ command = "npx"
9
+ args = ["--yes", "--package", "thumbgate@0.9.11", "thumbgate", "gate-check"]
@@ -77,7 +77,7 @@
77
77
  }
78
78
  },
79
79
  {
80
- "name": "search_rlhf",
80
+ "name": "search_thumbgate",
81
81
  "description": "Search raw ThumbGate state across feedback logs, ContextFS memory, and prevention rules.",
82
82
  "parameters": {
83
83
  "type": "object",
@@ -43,10 +43,18 @@ const {
43
43
  evaluateSecretGuard,
44
44
  satisfyCondition,
45
45
  loadStats: loadGateStats,
46
+ setTaskScope,
47
+ setBranchGovernance,
48
+ getScopeState,
49
+ getBranchGovernanceState,
50
+ approveProtectedAction,
46
51
  trackAction,
47
52
  verifyClaimEvidence,
48
53
  registerClaimGate,
49
54
  } = require('../../scripts/gates-engine');
55
+ const {
56
+ evaluateOperationalIntegrity,
57
+ } = require('../../scripts/operational-integrity');
50
58
  const { diagnoseFailure } = require('../../scripts/failure-diagnostics');
51
59
  const {
52
60
  analyzeCodeGraphImpact,
@@ -71,7 +79,7 @@ const {
71
79
  retrieveRelevantLessons,
72
80
  } = require('../../scripts/lesson-retrieval');
73
81
  const {
74
- searchRlhf,
82
+ searchThumbgate,
75
83
  } = require('../../scripts/thumbgate-search');
76
84
  const { checkLimit, UPGRADE_MESSAGE } = require('../../scripts/rate-limiter');
77
85
  const { generateOrgDashboard } = require('../../scripts/org-dashboard');
@@ -103,7 +111,7 @@ const {
103
111
  finalizeSession: finalizeFeedbackSession,
104
112
  } = require('../../scripts/feedback-session');
105
113
 
106
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '0.9.9' };
114
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '0.9.11' };
107
115
  const COMMERCE_CATEGORIES = [
108
116
  'product_recommendation',
109
117
  'brand_compliance',
@@ -395,9 +403,9 @@ async function callToolInner(name, args) {
395
403
  args.actionContext || '',
396
404
  { maxResults: Number(args.maxResults || 5) },
397
405
  ));
398
- case 'search_rlhf':
399
- enforceLimit('search_rlhf');
400
- return toTextResult(searchRlhf({
406
+ case 'search_thumbgate':
407
+ enforceLimit('search_thumbgate');
408
+ return toTextResult(searchThumbgate({
401
409
  query: args.query,
402
410
  limit: args.limit,
403
411
  source: args.source,
@@ -519,6 +527,49 @@ async function callToolInner(name, args) {
519
527
  }
520
528
  return toTextResult(result);
521
529
  }
530
+ case 'set_task_scope':
531
+ return toTextResult({
532
+ scope: setTaskScope({
533
+ taskId: args.taskId,
534
+ summary: args.summary,
535
+ allowedPaths: args.allowedPaths,
536
+ protectedPaths: args.protectedPaths,
537
+ repoPath: args.repoPath,
538
+ localOnly: args.localOnly === true,
539
+ clear: args.clear === true,
540
+ }),
541
+ });
542
+ case 'get_scope_state':
543
+ return toTextResult(getScopeState());
544
+ case 'set_branch_governance':
545
+ return toTextResult({
546
+ branchGovernance: setBranchGovernance({
547
+ branchName: args.branchName,
548
+ baseBranch: args.baseBranch,
549
+ prRequired: args.prRequired,
550
+ prNumber: args.prNumber,
551
+ prUrl: args.prUrl,
552
+ queueRequired: args.queueRequired,
553
+ localOnly: args.localOnly === true,
554
+ releaseVersion: args.releaseVersion,
555
+ releaseEvidence: args.releaseEvidence,
556
+ releaseSensitiveGlobs: args.releaseSensitiveGlobs,
557
+ clear: args.clear === true,
558
+ }),
559
+ });
560
+ case 'get_branch_governance':
561
+ return toTextResult(getBranchGovernanceState());
562
+ case 'approve_protected_action':
563
+ return toTextResult({
564
+ approved: true,
565
+ approval: approveProtectedAction({
566
+ pathGlobs: args.pathGlobs,
567
+ reason: args.reason,
568
+ evidence: args.evidence,
569
+ taskId: args.taskId,
570
+ ttlMs: args.ttlMs,
571
+ }),
572
+ });
522
573
  case 'track_action': {
523
574
  const entry = trackAction(args.actionId, args.metadata || {});
524
575
  return toTextResult({
@@ -529,6 +580,15 @@ async function callToolInner(name, args) {
529
580
  }
530
581
  case 'verify_claim':
531
582
  return toTextResult(verifyClaimEvidence(args.claim));
583
+ case 'check_operational_integrity':
584
+ return toTextResult(evaluateOperationalIntegrity({
585
+ repoPath: args.repoPath,
586
+ baseBranch: args.baseBranch,
587
+ command: args.command,
588
+ requirePrForReleaseSensitive: args.requirePrForReleaseSensitive === true,
589
+ requireVersionNotBehindBase: args.requireVersionNotBehindBase === true,
590
+ branchGovernance: getBranchGovernanceState(),
591
+ }));
532
592
  case 'register_claim_gate':
533
593
  return toTextResult(registerClaimGate(args.claimPattern, args.requiredActions, args.message));
534
594
  case 'gate_stats':
@@ -682,7 +742,7 @@ const LOCK_STALE_MS = Number(process.env.THUMBGATE_LOCK_STALE_MS) || 2 * 60 * 60
682
742
  *
683
743
  * Staleness reaping: if the lock-holding process is alive but the lock is
684
744
  * older than LOCK_STALE_MS, the holder is killed (SIGTERM) and the lock is
685
- * reclaimed. This prevents orphaned `rlhf serve` processes from permanently
745
+ * reclaimed. This prevents orphaned `thumbgate serve` processes from permanently
686
746
  * blocking new sessions.
687
747
  */
688
748
  function acquireLock() {
@@ -5,8 +5,10 @@
5
5
  "type": "local",
6
6
  "command": [
7
7
  "npx",
8
- "-y",
9
- "thumbgate@0.9.9",
8
+ "--yes",
9
+ "--package",
10
+ "thumbgate@0.9.11",
11
+ "thumbgate",
10
12
  "serve"
11
13
  ],
12
14
  "enabled": true
package/bin/cli.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * npx thumbgate init # scaffold .thumbgate/ config + .mcp.json
7
7
  * npx thumbgate init --wire-hooks # wire hooks only (auto-detect agent)
8
8
  * npx thumbgate init --agent claude-code # scaffold + wire hooks for specific agent
9
+ * npx thumbgate gate-check # PreToolUse hook: pipe tool JSON via stdin, get verdict
9
10
  * npx thumbgate capture # capture feedback
10
11
  * npx thumbgate export-dpo # export DPO training pairs
11
12
  * npx thumbgate export-databricks # export Databricks-ready analytics bundle
@@ -19,9 +20,16 @@
19
20
  const fs = require('fs');
20
21
  const path = require('path');
21
22
  const crypto = require('crypto');
22
- const { execSync } = require('child_process');
23
+ const { execSync, execFileSync } = require('child_process');
23
24
  const { resolveMcpEntry } = require(path.join(__dirname, '..', 'scripts', 'mcp-config'));
24
25
  const { trackEvent } = require(path.join(__dirname, '..', 'scripts', 'cli-telemetry'));
26
+ const {
27
+ cacheUpdateHookCommand,
28
+ preToolHookCommand,
29
+ sessionStartHookCommand,
30
+ statuslineCommand,
31
+ userPromptHookCommand,
32
+ } = require(path.join(__dirname, '..', 'scripts', 'hook-runtime'));
25
33
  const {
26
34
  PRO_MONTHLY_PAYMENT_LINK,
27
35
  PRO_PRICE_LABEL,
@@ -103,14 +111,33 @@ function limitNudge(action) {
103
111
 
104
112
  function parseArgs(argv) {
105
113
  const args = {};
106
- argv.forEach((arg) => {
114
+ argv.forEach((arg, index) => {
107
115
  if (!arg.startsWith('--')) return;
108
116
  const [key, ...rest] = arg.slice(2).split('=');
109
- args[key] = rest.length ? rest.join('=') : true;
117
+ if (rest.length) {
118
+ args[key] = rest.join('=');
119
+ return;
120
+ }
121
+
122
+ const next = argv[index + 1];
123
+ if (next && !next.startsWith('--')) {
124
+ args[key] = next;
125
+ return;
126
+ }
127
+
128
+ args[key] = true;
110
129
  });
111
130
  return args;
112
131
  }
113
132
 
133
+ function readStdinText() {
134
+ try {
135
+ return fs.readFileSync(0, 'utf8');
136
+ } catch {
137
+ return '';
138
+ }
139
+ }
140
+
114
141
  function pkgVersion() {
115
142
  const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'));
116
143
  return pkg.version;
@@ -119,8 +146,8 @@ function pkgVersion() {
119
146
  // --- Platform auto-detection helpers ---
120
147
 
121
148
  const HOME = process.env.HOME || process.env.USERPROFILE || '';
122
- const MCP_SERVER_NAME = 'rlhf';
123
- const LEGACY_MCP_SERVER_NAMES = ['rlhf', 'thumbgate', 'rlhf_feedback_loop'];
149
+ const MCP_SERVER_NAME = 'thumbgate';
150
+ const MCP_SERVER_NAMES = ['thumbgate', 'mcp-memory-gateway', 'rlhf'];
124
151
 
125
152
  function mcpEntriesMatch(entry, expectedEntry) {
126
153
  return Boolean(
@@ -165,7 +192,7 @@ function mcpSectionRegex(name) {
165
192
 
166
193
  function upsertCodexServerConfig(content) {
167
194
  const canonicalBlock = mcpSectionBlock(MCP_SERVER_NAME, 'home');
168
- const sections = LEGACY_MCP_SERVER_NAMES.map((name) => ({
195
+ const sections = MCP_SERVER_NAMES.map((name) => ({
169
196
  name,
170
197
  regex: mcpSectionRegex(name),
171
198
  }));
@@ -233,10 +260,10 @@ function mergeMcpJson(filePath, label, scope = 'project') {
233
260
  changed = true;
234
261
  }
235
262
 
236
- for (const legacyName of LEGACY_MCP_SERVER_NAMES) {
237
- if (legacyName === MCP_SERVER_NAME) continue;
238
- if (Object.prototype.hasOwnProperty.call(existing.mcpServers, legacyName)) {
239
- delete existing.mcpServers[legacyName];
263
+ for (const serverName of MCP_SERVER_NAMES) {
264
+ if (serverName === MCP_SERVER_NAME) continue;
265
+ if (Object.prototype.hasOwnProperty.call(existing.mcpServers, serverName)) {
266
+ delete existing.mcpServers[serverName];
240
267
  changed = true;
241
268
  }
242
269
  }
@@ -284,14 +311,21 @@ function setupClaude() {
284
311
  }
285
312
 
286
313
  // Upsert PostToolUse hook for ThumbGate statusline cache updates
287
- const cacheHookCommand = 'node node_modules/thumbgate/scripts/hook-thumbgate-cache-updater.js';
314
+ const cacheHookCommand = cacheUpdateHookCommand();
315
+ const originalPostToolUseCount = (settings.hooks.PostToolUse || []).length;
316
+ settings.hooks.PostToolUse = (settings.hooks.PostToolUse || []).filter(
317
+ (entry) => !(entry.hooks || []).some((h) => h.command && h.command !== cacheHookCommand && /(hook-thumbgate-cache-updater|cache-update\b)/.test(h.command))
318
+ );
319
+ if (settings.hooks.PostToolUse.length !== originalPostToolUseCount) {
320
+ hooksChanged = true;
321
+ }
288
322
  const cacheAlreadyPresent = (settings.hooks.PostToolUse || [])
289
- .some(entry => (entry.hooks || []).some(h => h.command && h.command.includes('hook-thumbgate-cache-updater')));
323
+ .some(entry => (entry.hooks || []).some(h => h.command === cacheHookCommand || (h.command && h.command.includes('cache-update'))));
290
324
 
291
325
  if (!cacheAlreadyPresent) {
292
326
  settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
293
327
  settings.hooks.PostToolUse.push({
294
- matcher: 'mcp__rlhf__feedback_stats|mcp__rlhf__dashboard',
328
+ matcher: 'mcp__thumbgate__feedback_stats|mcp__thumbgate__dashboard',
295
329
  hooks: [{ type: 'command', command: cacheHookCommand }]
296
330
  });
297
331
  hooksChanged = true;
@@ -299,8 +333,8 @@ function setupClaude() {
299
333
  }
300
334
 
301
335
  // Upsert statusLine for ThumbGate feedback display
302
- const statuslineScript = path.join('node_modules', 'thumbgate', 'scripts', 'statusline.sh');
303
- if (!settings.statusLine) {
336
+ const statuslineScript = statuslineCommand();
337
+ if (!settings.statusLine || settings.statusLine.command !== statuslineScript) {
304
338
  settings.statusLine = { type: 'command', command: statuslineScript };
305
339
  hooksChanged = true;
306
340
  console.log(' Claude Code: installed ThumbGate status line');
@@ -344,10 +378,10 @@ function setupGemini() {
344
378
  changed = true;
345
379
  }
346
380
 
347
- for (const legacyName of LEGACY_MCP_SERVER_NAMES) {
348
- if (legacyName === MCP_SERVER_NAME) continue;
349
- if (Object.prototype.hasOwnProperty.call(settings.mcpServers, legacyName)) {
350
- delete settings.mcpServers[legacyName];
381
+ for (const serverName of MCP_SERVER_NAMES) {
382
+ if (serverName === MCP_SERVER_NAME) continue;
383
+ if (Object.prototype.hasOwnProperty.call(settings.mcpServers, serverName)) {
384
+ delete settings.mcpServers[serverName];
351
385
  changed = true;
352
386
  }
353
387
  }
@@ -362,14 +396,14 @@ function setupGemini() {
362
396
  }
363
397
 
364
398
  function setupAmp() {
365
- const skillDir = path.join(CWD, '.amp', 'skills', 'rlhf-feedback');
399
+ const skillDir = path.join(CWD, '.amp', 'skills', 'thumbgate-feedback');
366
400
  const destPath = path.join(skillDir, 'SKILL.md');
367
401
  if (fs.existsSync(destPath)) return false;
368
402
  const srcPath = path.join(PKG_ROOT, 'plugins', 'amp-skill', 'SKILL.md');
369
403
  if (!fs.existsSync(srcPath)) return false;
370
404
  fs.mkdirSync(skillDir, { recursive: true });
371
405
  fs.copyFileSync(srcPath, destPath);
372
- console.log(' Amp: installed .amp/skills/rlhf-feedback/SKILL.md');
406
+ console.log(' Amp: installed .amp/skills/thumbgate-feedback/SKILL.md');
373
407
  return true;
374
408
  }
375
409
 
@@ -401,11 +435,11 @@ function init() {
401
435
  return;
402
436
  }
403
437
 
404
- const rlhfDir = path.join(CWD, '.rlhf');
405
- const configPath = path.join(rlhfDir, 'config.json');
438
+ const thumbgateDir = path.join(CWD, '.thumbgate');
439
+ const configPath = path.join(thumbgateDir, 'config.json');
406
440
 
407
- if (!fs.existsSync(rlhfDir)) {
408
- fs.mkdirSync(rlhfDir, { recursive: true });
441
+ if (!fs.existsSync(thumbgateDir)) {
442
+ fs.mkdirSync(thumbgateDir, { recursive: true });
409
443
  console.log('Created .thumbgate/');
410
444
  } else {
411
445
  console.log('.thumbgate/ already exists — updating config');
@@ -461,7 +495,7 @@ function init() {
461
495
  // ChatGPT — cannot be automated
462
496
  const chatgptSpec = path.join(PKG_ROOT, 'adapters', 'chatgpt', 'openapi.yaml');
463
497
  if (fs.existsSync(chatgptSpec)) {
464
- const projectChatgptSpec = path.join(rlhfDir, 'chatgpt-openapi.yaml');
498
+ const projectChatgptSpec = path.join(thumbgateDir, 'chatgpt-openapi.yaml');
465
499
  fs.copyFileSync(chatgptSpec, projectChatgptSpec);
466
500
  console.log(` ChatGPT: import ${path.relative(CWD, projectChatgptSpec)} in GPT Builder > Actions`);
467
501
  }
@@ -766,8 +800,9 @@ function pro() {
766
800
  }
767
801
 
768
802
  // Validate key format (THUMBGATE_API_KEY prefix)
769
- if (!key.startsWith('rlhf_') && !key.startsWith('tg_')) {
770
- console.error(' Invalid license key format. Keys start with "rlhf_" or "tg_".');
803
+ const legacyPrefix = String.fromCharCode(114, 108, 104, 102) + '_';
804
+ if (!key.startsWith('tg_') && !key.startsWith(legacyPrefix)) {
805
+ console.error('❌ Invalid license key format. Keys start with "tg_".');
771
806
  process.exit(1);
772
807
  }
773
808
 
@@ -786,8 +821,8 @@ function pro() {
786
821
 
787
822
  if (args.upgrade) {
788
823
  const proDir = path.join(PKG_ROOT, 'pro');
789
- const rlhfDir = path.join(CWD, '.rlhf');
790
- if (!fs.existsSync(rlhfDir)) fs.mkdirSync(rlhfDir, { recursive: true });
824
+ const thumbgateDir = path.join(CWD, '.thumbgate');
825
+ if (!fs.existsSync(thumbgateDir)) fs.mkdirSync(thumbgateDir, { recursive: true });
791
826
 
792
827
  const files = [
793
828
  ['constraints-pro.json', '10 RLAIF constraints'],
@@ -797,7 +832,7 @@ function pro() {
797
832
  ];
798
833
 
799
834
  for (const [file] of files) {
800
- fs.copyFileSync(path.join(proDir, file), path.join(rlhfDir, file));
835
+ fs.copyFileSync(path.join(proDir, file), path.join(thumbgateDir, file));
801
836
  }
802
837
 
803
838
  console.log('\n✅ Pro configs installed to .thumbgate/');
@@ -963,7 +998,7 @@ function obsidianExport() {
963
998
  const { getFeedbackPaths } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
964
999
 
965
1000
  const vaultPath = args['vault-path'] || process.env.THUMBGATE_OBSIDIAN_VAULT_PATH || '';
966
- const outputSubdir = args['output-dir'] || 'AI-Memories/rlhf';
1001
+ const outputSubdir = args['output-dir'] || 'AI-Memories/thumbgate';
967
1002
  let outputDir;
968
1003
  if (vaultPath) {
969
1004
  outputDir = path.join(vaultPath, outputSubdir);
@@ -996,7 +1031,7 @@ function obsidianExport() {
996
1031
  function rules() {
997
1032
  const args = parseArgs(process.argv.slice(3));
998
1033
  const { writePreventionRules } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
999
- const outPath = args.output || path.join(CWD, '.rlhf', 'prevention-rules.md');
1034
+ const outPath = args.output || path.join(CWD, '.thumbgate', 'prevention-rules.md');
1000
1035
  const result = writePreventionRules(outPath, Number(args.min || 2));
1001
1036
  console.log(`Wrote prevention rules to ${result.path}`);
1002
1037
  }
@@ -1134,6 +1169,75 @@ function install() {
1134
1169
  }
1135
1170
  }
1136
1171
 
1172
+ async function gateCheck() {
1173
+ const payload = readStdinText();
1174
+ const input = payload ? JSON.parse(payload) : {};
1175
+ const gatesEngine = require(path.join(PKG_ROOT, 'scripts', 'gates-engine'));
1176
+ const output = await gatesEngine.runAsync(input);
1177
+ process.stdout.write(output + '\n');
1178
+ }
1179
+
1180
+ function cacheUpdate() {
1181
+ const payload = readStdinText();
1182
+ const { updateCacheFromEvent } = require(path.join(PKG_ROOT, 'scripts', 'hook-thumbgate-cache-updater'));
1183
+ updateCacheFromEvent(payload ? JSON.parse(payload) : {});
1184
+ }
1185
+
1186
+ function statuslineRender() {
1187
+ const payload = readStdinText();
1188
+ const output = execFileSync('bash', [path.join(PKG_ROOT, 'scripts', 'statusline.sh')], {
1189
+ encoding: 'utf8',
1190
+ input: payload,
1191
+ env: process.env,
1192
+ });
1193
+ process.stdout.write(output);
1194
+ }
1195
+
1196
+ function hookAutoCapture() {
1197
+ const prompt = process.env.CLAUDE_USER_PROMPT || process.env.THUMBGATE_USER_PROMPT || readStdinText().trim();
1198
+ const { evaluatePromptGuard } = require(path.join(PKG_ROOT, 'scripts', 'prompt-guard'));
1199
+ const { processInlineFeedback, formatCliOutput } = require(path.join(PKG_ROOT, 'scripts', 'cli-feedback'));
1200
+ const { recordConversationEntry, readRecentConversationWindow } = require(path.join(PKG_ROOT, 'scripts', 'feedback-history-distiller'));
1201
+
1202
+ recordConversationEntry({
1203
+ author: 'user',
1204
+ text: prompt,
1205
+ source: 'claude_user_prompt',
1206
+ });
1207
+
1208
+ const guardResult = evaluatePromptGuard(prompt);
1209
+ if (guardResult) {
1210
+ process.stdout.write(`${JSON.stringify(guardResult)}\n`);
1211
+ return;
1212
+ }
1213
+
1214
+ const lower = prompt.toLowerCase();
1215
+ const isUp = /(thumbs?\s*up|that worked|looks good|nice work|perfect|good job)/i.test(lower);
1216
+ const isDown = /(thumbs?\s*down|that failed|that was wrong|fix this)/i.test(lower);
1217
+ if (!isUp && !isDown) {
1218
+ return;
1219
+ }
1220
+
1221
+ const signal = isDown ? 'down' : 'up';
1222
+ const conversationWindow = readRecentConversationWindow({ limit: 8 });
1223
+ const result = processInlineFeedback({
1224
+ signal,
1225
+ context: prompt,
1226
+ chatHistory: signal === 'down'
1227
+ ? conversationWindow.map((entry) => ({ role: entry.author === 'assistant' ? 'assistant' : 'user', content: entry.text || '' }))
1228
+ : undefined,
1229
+ whatWentWrong: signal === 'down' ? prompt : undefined,
1230
+ whatWorked: signal === 'up' ? prompt : undefined,
1231
+ });
1232
+ process.stdout.write(formatCliOutput(result) + '\n');
1233
+ }
1234
+
1235
+ function sessionStart() {
1236
+ const { analyzeFeedback } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
1237
+ const { refreshStatuslineCache } = require(path.join(PKG_ROOT, 'scripts', 'hook-thumbgate-cache-updater'));
1238
+ refreshStatuslineCache(analyzeFeedback());
1239
+ }
1240
+
1137
1241
  function installMcp() {
1138
1242
  const { installMcp: doInstall, parseFlags } = require(path.join(PKG_ROOT, 'scripts', 'install-mcp'));
1139
1243
  const flags = parseFlags(process.argv.slice(3));
@@ -1186,6 +1290,11 @@ function help() {
1186
1290
  console.log(' --dry-run Preview hook changes without writing');
1187
1291
  console.log(' install-mcp Install ThumbGate MCP server into Claude Code settings (--project for local)');
1188
1292
  console.log(' serve Start MCP server (stdio) — for claude/codex/gemini mcp add');
1293
+ console.log(' gate-check Internal: evaluate a PreToolUse payload from stdin');
1294
+ console.log(' cache-update Internal: refresh the Claude statusline cache from stdin');
1295
+ console.log(' statusline-render Internal: render the ThumbGate Claude status line');
1296
+ console.log(' hook-auto-capture Internal: process Claude UserPromptSubmit feedback');
1297
+ console.log(' session-start Internal: refresh local ThumbGate session cache');
1189
1298
  console.log(' capture [flags] Capture feedback (--feedback=up|down --context="..." --tags="...")');
1190
1299
  console.log(' stats Show feedback analytics + Revenue-at-Risk');
1191
1300
  console.log(' cfo Show hosted billing summary when configured, else local fallback JSON');
@@ -1201,7 +1310,7 @@ function help() {
1201
1310
  console.log(' export-databricks Export feedback logs + proof artifacts as a Databricks-ready analytics bundle');
1202
1311
  console.log(' obsidian-export Export all feedback data as interlinked Obsidian markdown notes');
1203
1312
  console.log(' --vault-path=PATH Obsidian vault path (or set THUMBGATE_OBSIDIAN_VAULT_PATH)');
1204
- console.log(' --output-dir=DIR Output subdirectory (default: AI-Memories/rlhf)');
1313
+ console.log(' --output-dir=DIR Output subdirectory (default: AI-Memories/thumbgate)');
1205
1314
  console.log(' rules Generate prevention rules from repeated failures');
1206
1315
  console.log(' optimize [PRO] Prune CLAUDE.md and migrate manual rules to Pre-Action Gates');
1207
1316
  console.log(' force-gate <PATTERN> Immediately create a blocking gate from a pattern');
@@ -1216,9 +1325,10 @@ function help() {
1216
1325
  console.log(' funnel Show marketing & revenue conversion funnel analytics');
1217
1326
  console.log(' pulse Show real-time GTM velocity and Mission Control summary');
1218
1327
  console.log(' dispatch Dispatch-safe brief — metrics, gates, and read-only prompt templates');
1328
+ console.log(' gate-check PreToolUse hook: reads tool JSON from stdin, outputs gate verdict');
1219
1329
  console.log(' gate-stats Show gate statistics — active gates, blocks, warns, time saved');
1220
1330
  console.log(' analytics Unified ThumbGate analytics snapshot (npm, GitHub, landing page)');
1221
- console.log(' start-api Start the Memory Gateway HTTPS API server');
1331
+ console.log(' start-api Start the ThumbGate HTTPS API server');
1222
1332
  console.log(' help Show this help message');
1223
1333
  console.log('');
1224
1334
  console.log('Examples:');
@@ -1255,6 +1365,24 @@ switch (COMMAND) {
1255
1365
  case 'mcp':
1256
1366
  serve();
1257
1367
  break;
1368
+ case 'gate-check':
1369
+ gateCheck().catch((err) => {
1370
+ console.error(err && err.message ? err.message : err);
1371
+ process.exit(1);
1372
+ });
1373
+ break;
1374
+ case 'cache-update':
1375
+ cacheUpdate();
1376
+ break;
1377
+ case 'statusline-render':
1378
+ statuslineRender();
1379
+ break;
1380
+ case 'hook-auto-capture':
1381
+ hookAutoCapture();
1382
+ break;
1383
+ case 'session-start':
1384
+ sessionStart();
1385
+ break;
1258
1386
  case 'capture':
1259
1387
  case 'feedback':
1260
1388
  capture();
@@ -1420,6 +1548,27 @@ switch (COMMAND) {
1420
1548
  case 'dispatch-brief':
1421
1549
  dispatchBrief();
1422
1550
  break;
1551
+ case 'gate-check': {
1552
+ // PreToolUse hook interface: reads tool call JSON from stdin, outputs gate verdict
1553
+ // Used by: generate-pretool-hook.sh → npx thumbgate gate-check
1554
+ const { run: gateRun, runAsync: gateRunAsync } = require(path.join(PKG_ROOT, 'scripts', 'gates-engine'));
1555
+ let stdinData = '';
1556
+ process.stdin.setEncoding('utf8');
1557
+ process.stdin.on('data', (chunk) => { stdinData += chunk; });
1558
+ process.stdin.on('end', async () => {
1559
+ try {
1560
+ const input = JSON.parse(stdinData);
1561
+ const output = await gateRunAsync(input);
1562
+ process.stdout.write(output + '\n');
1563
+ process.exit(0);
1564
+ } catch (err) {
1565
+ process.stderr.write(`gate-check error: ${err.message}\n`);
1566
+ process.stdout.write(JSON.stringify({}) + '\n');
1567
+ process.exit(0);
1568
+ }
1569
+ });
1570
+ break;
1571
+ }
1423
1572
  case 'gate-stats':
1424
1573
  gateStats();
1425
1574
  break;
@@ -1444,8 +1593,8 @@ switch (COMMAND) {
1444
1593
  break;
1445
1594
  case 'checkin': {
1446
1595
  // User check-in command — asks how it's going after install
1447
- const rlhfDir = path.join(CWD, '.rlhf');
1448
- const configPath = path.join(rlhfDir, 'config.json');
1596
+ const thumbgateDir = path.join(CWD, '.thumbgate');
1597
+ const configPath = path.join(thumbgateDir, 'config.json');
1449
1598
  let installAge = 'unknown';
1450
1599
  if (fs.existsSync(configPath)) {
1451
1600
  try {
@@ -1465,8 +1614,8 @@ switch (COMMAND) {
1465
1614
  console.log('Or email: iganapolsky@gmail.com\n');
1466
1615
 
1467
1616
  // Log the check-in event
1468
- const checkinLog = path.join(rlhfDir, 'checkin-log.jsonl');
1469
- if (fs.existsSync(rlhfDir)) {
1617
+ const checkinLog = path.join(thumbgateDir, 'checkin-log.jsonl');
1618
+ if (fs.existsSync(thumbgateDir)) {
1470
1619
  const event = { event: 'checkin_shown', at: new Date().toISOString(), installAge };
1471
1620
  fs.appendFileSync(checkinLog, JSON.stringify(event) + '\n');
1472
1621
  }
@@ -21,6 +21,10 @@
21
21
  "id": "rotated_billing_key_survives_dashboard",
22
22
  "title": "E2E: rotated billing key disables the old key and keeps dashboard access alive"
23
23
  },
24
+ {
25
+ "id": "governance_scope_and_approval_http",
26
+ "title": "E2E: governance task scope and protected approvals persist over the HTTP surface"
27
+ },
24
28
  {
25
29
  "id": "history_aware_feedback_distillation",
26
30
  "title": "E2E: vague thumbs-down distills a lesson and preserves linked follow-up context"