thumbgate 1.27.19 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/.claude/commands/dashboard.md +15 -0
  2. package/.claude/commands/thumbgate-blocked.md +27 -0
  3. package/.claude/commands/thumbgate-dashboard.md +15 -0
  4. package/.claude/commands/thumbgate-doctor.md +30 -0
  5. package/.claude/commands/thumbgate-guard.md +36 -0
  6. package/.claude/commands/thumbgate-protect.md +30 -0
  7. package/.claude/commands/thumbgate-rules.md +30 -0
  8. package/.claude-plugin/plugin.json +1 -1
  9. package/.well-known/mcp/server-card.json +1 -1
  10. package/README.md +63 -45
  11. package/adapters/claude/.mcp.json +2 -2
  12. package/adapters/gcp/dfcx-webhook-gate.js +295 -0
  13. package/adapters/letta/README.md +41 -0
  14. package/adapters/letta/thumbgate-letta-adapter.js +133 -0
  15. package/adapters/mcp/server-stdio.js +1 -1
  16. package/adapters/opencode/opencode.json +1 -1
  17. package/adapters/policy-engine/ethicore-guardian-client.js +68 -0
  18. package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +260 -0
  19. package/bench/observability-eval-suite.json +26 -0
  20. package/bin/cli.js +52 -9
  21. package/bin/dashboard-cli.js +7 -0
  22. package/bin/postinstall.js +14 -23
  23. package/commands/dashboard.md +15 -0
  24. package/commands/thumbgate-dashboard.md +15 -0
  25. package/config/entitlement-public-keys.json +6 -0
  26. package/config/gates/default.json +1 -1
  27. package/config/github-about.json +2 -2
  28. package/config/merge-quality-checks.json +3 -0
  29. package/package.json +233 -100
  30. package/public/about.html +159 -0
  31. package/public/agent-manager.html +179 -0
  32. package/public/agents-cost-savings.html +153 -0
  33. package/public/ai-malpractice-prevention.html +818 -0
  34. package/public/assets/brand/github-social-preview.png +0 -0
  35. package/public/assets/brand/thumbgate-icon-512.png +0 -0
  36. package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
  37. package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
  38. package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
  39. package/public/assets/brand/thumbgate-logo-transparent.svg +28 -0
  40. package/public/assets/brand/thumbgate-mark-inline-v3.svg +18 -0
  41. package/public/assets/brand/thumbgate-mark-pro.svg +23 -0
  42. package/public/assets/brand/thumbgate-mark-team.svg +26 -0
  43. package/public/assets/brand/thumbgate-mark.svg +21 -0
  44. package/public/assets/brand/thumbgate-wordmark.svg +20 -0
  45. package/public/assets/claude-thumbgate-statusbar.svg +8 -0
  46. package/public/assets/codex-thumbgate-statusbar-test.svg +9 -0
  47. package/public/assets/legal-intake-control-flow.svg +66 -0
  48. package/public/blog.html +12 -9
  49. package/public/brand/thumbgate-mark.svg +19 -0
  50. package/public/brand/thumbgate-og.svg +16 -0
  51. package/public/chatgpt-app.html +330 -0
  52. package/public/codex-enterprise.html +123 -0
  53. package/public/codex-plugin.html +5 -5
  54. package/public/compare.html +10 -10
  55. package/public/dashboard.html +1 -1
  56. package/public/diagnostic.html +345 -0
  57. package/public/federal.html +2 -2
  58. package/public/guide.html +14 -14
  59. package/public/index.html +81 -70
  60. package/public/install.html +193 -0
  61. package/public/js/buyer-intent.js +672 -0
  62. package/public/learn.html +4 -17
  63. package/public/numbers.html +2 -2
  64. package/public/pricing.html +392 -0
  65. package/public/pro.html +2 -2
  66. package/scripts/action-receipts.js +324 -0
  67. package/scripts/activation-quickstart.js +187 -0
  68. package/scripts/agent-operations-planner.js +621 -0
  69. package/scripts/agent-reward-model.js +13 -0
  70. package/scripts/ai-component-inventory.js +367 -0
  71. package/scripts/async-eval-observability.js +236 -0
  72. package/scripts/audit.js +65 -0
  73. package/scripts/aws-blocks-guardrails.js +272 -0
  74. package/scripts/bayes-optimal-gate.js +6 -1
  75. package/scripts/classifier-routing.js +130 -0
  76. package/scripts/cli-feedback.js +17 -1
  77. package/scripts/commercial-offer.js +3 -3
  78. package/scripts/dashboard-chat.js +332 -0
  79. package/scripts/entitlement.js +250 -0
  80. package/scripts/export-databricks-bundle.js +5 -0
  81. package/scripts/export-dpo-pairs.js +6 -0
  82. package/scripts/export-hf-dataset.js +5 -0
  83. package/scripts/feedback-aggregate.js +281 -0
  84. package/scripts/feedback-sanitizer.js +105 -0
  85. package/scripts/gates-engine.js +15 -3
  86. package/scripts/hook-stop-anti-claim.js +301 -0
  87. package/scripts/imperative-detector.js +85 -0
  88. package/scripts/install-shim.js +87 -0
  89. package/scripts/intervention-policy.js +13 -0
  90. package/scripts/mcp-oauth.js +293 -0
  91. package/scripts/noop-detect.js +285 -0
  92. package/scripts/parallel-workflow-orchestrator.js +293 -0
  93. package/scripts/plan-gate.js +243 -0
  94. package/scripts/plausible-domain-config.js +99 -0
  95. package/scripts/pr-manager.js +9 -22
  96. package/scripts/pro-local-dashboard.js +198 -0
  97. package/scripts/qa-scenario-planner.js +136 -0
  98. package/scripts/repeat-metric.js +137 -0
  99. package/scripts/risk-scorer.js +6 -0
  100. package/scripts/secret-fixture-tokens.js +61 -0
  101. package/scripts/secret-redaction.js +166 -0
  102. package/scripts/self-harness-optimizer.js +141 -0
  103. package/scripts/self-healing-check.js +193 -0
  104. package/scripts/self-protection.js +90 -0
  105. package/scripts/seo-gsd.js +2 -2
  106. package/scripts/silent-failure-cluster.js +531 -0
  107. package/scripts/statusline-cache-read.js +57 -0
  108. package/scripts/sync-telemetry-from-prod.js +374 -0
  109. package/scripts/thompson-sampling.js +11 -2
  110. package/scripts/tool-contract-validator.js +76 -0
  111. package/scripts/trajectory-scorer.js +63 -0
  112. package/scripts/verify-marketing-pages-deployed.js +212 -0
  113. package/scripts/visitor-journey.js +172 -0
  114. package/src/api/server.js +81 -10
  115. package/.claude-plugin/marketplace.json +0 -85
  116. package/adapters/chatgpt/openapi.yaml +0 -1707
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { normalizeProviderAction } = require('../../scripts/provider-action-normalizer');
5
+
6
+ const BLOCK_DECISIONS = new Set([
7
+ 'block',
8
+ 'blocked',
9
+ 'deny',
10
+ 'denied',
11
+ 'disallow',
12
+ 'disallowed',
13
+ 'fail',
14
+ 'failed',
15
+ 'forbid',
16
+ 'forbidden',
17
+ 'reject',
18
+ 'rejected',
19
+ 'unsafe',
20
+ 'violation',
21
+ ]);
22
+
23
+ const REVIEW_DECISIONS = new Set([
24
+ 'approval',
25
+ 'approval-required',
26
+ 'approval_required',
27
+ 'approve',
28
+ 'human-review',
29
+ 'human_review',
30
+ 'manual-review',
31
+ 'manual_review',
32
+ 'review',
33
+ 'requires-approval',
34
+ 'requires_approval',
35
+ 'requires-review',
36
+ 'requires_review',
37
+ ]);
38
+
39
+ const ALLOW_DECISIONS = new Set([
40
+ 'accept',
41
+ 'accepted',
42
+ 'allow',
43
+ 'allowed',
44
+ 'ok',
45
+ 'pass',
46
+ 'passed',
47
+ 'permit',
48
+ 'permitted',
49
+ 'safe',
50
+ ]);
51
+
52
+ function asObject(value) {
53
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
54
+ }
55
+
56
+ function asArray(value) {
57
+ return Array.isArray(value) ? value : [];
58
+ }
59
+
60
+ function firstString(...values) {
61
+ for (const value of values) {
62
+ const text = String(value || '').trim();
63
+ if (text) return text;
64
+ }
65
+ return '';
66
+ }
67
+
68
+ function normalizeDecisionToken(value) {
69
+ return String(value || '')
70
+ .trim()
71
+ .toLowerCase()
72
+ .replace(/\s+/g, '-');
73
+ }
74
+
75
+ function normalizeEvidence(value) {
76
+ const direct = asArray(value.evidence);
77
+ const citations = asArray(value.citations);
78
+ const violations = asArray(value.violations);
79
+ const reasons = asArray(value.reasons);
80
+ const reasoning = asArray(value.reasoning);
81
+ const threatTypes = asArray(value.threat_types || value.threatTypes)
82
+ .map((threatType) => ({
83
+ code: String(threatType || '').trim(),
84
+ message: `Threat type: ${String(threatType || '').trim()}`,
85
+ source: 'guardian',
86
+ severity: firstString(value.threat_level, value.threatLevel),
87
+ }));
88
+ return [...direct, ...citations, ...violations, ...reasons, ...reasoning, ...threatTypes]
89
+ .map((entry) => {
90
+ if (typeof entry === 'string') return { text: entry };
91
+ const object = asObject(entry);
92
+ if (!Object.keys(object).length) return null;
93
+ return {
94
+ id: firstString(object.id, object.ruleId, object.rule_id, object.code),
95
+ text: firstString(object.text, object.reason, object.message, object.description, object.title),
96
+ source: firstString(object.source, object.provider, object.policy),
97
+ severity: firstString(object.severity, object.level),
98
+ raw: object,
99
+ };
100
+ })
101
+ .filter(Boolean);
102
+ }
103
+
104
+ function extractPolicyDecision(input = {}) {
105
+ const event = asObject(input);
106
+ for (const candidate of [
107
+ event.policyDecision,
108
+ event.policy_decision,
109
+ event.guardrailResult,
110
+ event.guardrail_result,
111
+ event.result,
112
+ ]) {
113
+ const object = asObject(candidate);
114
+ if (Object.keys(object).length) return object;
115
+ }
116
+ return event;
117
+ }
118
+
119
+ function classifyPolicyDecision(input = {}) {
120
+ const value = extractPolicyDecision(input);
121
+ const token = normalizeDecisionToken(firstString(
122
+ value.decision,
123
+ value.action,
124
+ value.status,
125
+ value.result,
126
+ value.verdict,
127
+ value.outcome,
128
+ value.effect,
129
+ value.recommended_action,
130
+ value.recommendedAction,
131
+ ));
132
+
133
+ if (
134
+ value.allowed === false
135
+ || value.is_safe === false
136
+ || value.isSafe === false
137
+ || value.accepted === false
138
+ || value.blocked === true
139
+ || value.denied === true
140
+ || BLOCK_DECISIONS.has(token)
141
+ ) {
142
+ return 'block';
143
+ }
144
+ if (
145
+ value.requiresApproval === true
146
+ || value.requires_approval === true
147
+ || value.reviewRequired === true
148
+ || value.review_required === true
149
+ || REVIEW_DECISIONS.has(token)
150
+ ) {
151
+ return 'approval_required';
152
+ }
153
+ if (
154
+ value.allowed === true
155
+ || value.is_safe === true
156
+ || value.isSafe === true
157
+ || value.accepted === true
158
+ || ALLOW_DECISIONS.has(token)
159
+ ) {
160
+ return 'allow';
161
+ }
162
+ return 'unknown';
163
+ }
164
+
165
+ function normalizePolicyDecision(input = {}, options = {}) {
166
+ const value = extractPolicyDecision(input);
167
+ const decision = classifyPolicyDecision(value);
168
+ const source = firstString(
169
+ options.source,
170
+ value.source,
171
+ value.provider,
172
+ value.engine,
173
+ value.policyEngine,
174
+ value.policy_engine,
175
+ 'policy-engine'
176
+ );
177
+ const reason = firstString(
178
+ value.reason,
179
+ value.message,
180
+ value.explanation,
181
+ value.summary,
182
+ asArray(value.reasoning).join('; '),
183
+ asArray(value.reasons).join('; '),
184
+ decision === 'unknown' ? 'Policy engine returned an unknown decision; approval required before execution.' : ''
185
+ );
186
+
187
+ return {
188
+ allowed: decision === 'allow',
189
+ blocked: decision === 'block',
190
+ approvalRequired: decision === 'approval_required' || decision === 'unknown',
191
+ decision,
192
+ reason,
193
+ source,
194
+ confidence: Number.isFinite(Number(value.confidence)) ? Number(value.confidence) : null,
195
+ policyId: firstString(value.policyId, value.policy_id, value.ruleId, value.rule_id, value.id),
196
+ severity: firstString(value.severity, value.level, value.threat_level, value.threatLevel),
197
+ score: Number.isFinite(Number(value.score))
198
+ ? Number(value.score)
199
+ : (Number.isFinite(Number(value.threat_score)) ? Number(value.threat_score) : null),
200
+ evidence: normalizeEvidence(value),
201
+ raw: value,
202
+ };
203
+ }
204
+
205
+ function normalizePolicyAction(input = {}) {
206
+ const event = asObject(input);
207
+ return {
208
+ ...normalizeProviderAction({
209
+ ...event,
210
+ provider: firstString(event.provider, event.agentRuntime, event.runtime, 'policy-engine'),
211
+ toolName: firstString(event.toolName, event.tool_name, event.name),
212
+ input: asObject(event.toolInput || event.input || event.arguments || event.args),
213
+ }),
214
+ policyContext: asObject(event.policyContext || event.policy_context),
215
+ };
216
+ }
217
+
218
+ function createPolicyEngineGuard({
219
+ policyCheck,
220
+ executeTool,
221
+ gateCheck,
222
+ onDecision,
223
+ source = 'policy-engine',
224
+ } = {}) {
225
+ if (typeof policyCheck !== 'function') {
226
+ throw new TypeError('createPolicyEngineGuard requires a policyCheck function');
227
+ }
228
+ if (typeof executeTool !== 'function') {
229
+ throw new TypeError('createPolicyEngineGuard requires an executeTool function');
230
+ }
231
+
232
+ return async function guardedPolicyTool(input = {}) {
233
+ const normalizedAction = normalizePolicyAction(input);
234
+ const policyDecision = normalizePolicyDecision(await policyCheck(normalizedAction), { source });
235
+ const gateDecision = typeof gateCheck === 'function'
236
+ ? normalizePolicyDecision(await gateCheck({ normalizedAction, policyDecision }), { source: 'thumbgate' })
237
+ : null;
238
+ const effectiveDecision = gateDecision && !gateDecision.allowed ? gateDecision : policyDecision;
239
+
240
+ if (typeof onDecision === 'function') {
241
+ await onDecision({ normalizedAction, policyDecision, gateDecision, effectiveDecision });
242
+ }
243
+
244
+ if (!effectiveDecision.allowed) {
245
+ const error = new Error(effectiveDecision.reason || 'ThumbGate blocked this action before execution.');
246
+ error.code = effectiveDecision.approvalRequired ? 'THUMBGATE_APPROVAL_REQUIRED' : 'THUMBGATE_BLOCKED';
247
+ error.thumbgate = { normalizedAction, policyDecision, gateDecision, effectiveDecision };
248
+ throw error;
249
+ }
250
+
251
+ return executeTool(input, { normalizedAction, policyDecision, gateDecision, effectiveDecision });
252
+ };
253
+ }
254
+
255
+ module.exports = {
256
+ createPolicyEngineGuard,
257
+ extractPolicyDecision,
258
+ normalizePolicyAction,
259
+ normalizePolicyDecision,
260
+ };
@@ -0,0 +1,26 @@
1
+ {
2
+ "cases": [
3
+ {
4
+ "id": "checkout-link-grounding",
5
+ "traceId": "trace_checkout_001",
6
+ "question": "Is the Pro checkout link working?",
7
+ "response": "The Pro checkout link is working because the checkout diagnostic confirms Stripe Pro checkout is reachable.",
8
+ "retrievedContexts": [
9
+ "Stripe Pro checkout diagnostic confirms the Pro checkout link is reachable and returns a valid checkout target.",
10
+ "The workflow sprint checkout is intake-led and should not expose a stale payment link."
11
+ ],
12
+ "reference": "The Pro checkout link is reachable when the Stripe diagnostic passes."
13
+ },
14
+ {
15
+ "id": "letta-tool-gate",
16
+ "traceId": "trace_letta_001",
17
+ "question": "Should Letta execute a force push tool call?",
18
+ "response": "ThumbGate blocks force push before execution for Letta tool calls.",
19
+ "retrievedContexts": [
20
+ "ThumbGate's Letta adapter wraps Letta tool calls and runs gate-check before the executor is called.",
21
+ "Force push to main is a blocked high-risk git action."
22
+ ],
23
+ "reference": "ThumbGate blocks high-risk Letta tool calls before execution."
24
+ }
25
+ ]
26
+ }
package/bin/cli.js CHANGED
@@ -134,7 +134,7 @@ function upgradeNudge() {
134
134
  '\n Team rollout: start with the $499 Workflow Hardening Diagnostic\n' +
135
135
  ` ${diagnosticUrl}\n` +
136
136
  `\n Solo side lane: Pro — ${PRO_PRICE_LABEL}\n` +
137
- ' Keeps lessons, rules, and the dashboard synced across machines and agent runtimes.\n' +
137
+ ' Removes solo caps; adds personal recall, dashboard proof, exports, and managed adapters.\n' +
138
138
  ` ${pricingUrl}\n\n`
139
139
  );
140
140
  }
@@ -234,9 +234,9 @@ function proNudge(context) {
234
234
  const checkoutUrl = checkoutUrlFor('cli_nudge', context || COMMAND || 'general');
235
235
  const pricingUrl = pricingUrlFor('cli_nudge', context || COMMAND || 'general');
236
236
  const messages = [
237
- `\n 💡 Pro (${PRO_PRICE_LABEL}): keep lessons, rules, and dashboard state synced across machines and agent runtimes.\n See pricing: ${pricingUrl}\n`,
238
- `\n 💡 You just taught ThumbGate something locally. Pro keeps that lesson alive on every laptop, CI box, and agent runtime.\n See pricing: ${pricingUrl}\n`,
239
- `\n 💡 ThumbGate Pro syncs lessons/rules across Claude, Codex, Cursor, containers, and CI. ${PRO_PRICE_LABEL}.\n Start Pro: ${checkoutUrl}\n`,
237
+ `\n 💡 Pro (${PRO_PRICE_LABEL}): personal recall, dashboard proof, exports, and managed adapters.\n See pricing: ${pricingUrl}\n`,
238
+ `\n 💡 You just taught ThumbGate something locally. Pro keeps personal lessons searchable and exportable without free-tier caps.\n See pricing: ${pricingUrl}\n`,
239
+ `\n 💡 ThumbGate Pro maintains adapter coverage across Claude, Codex, Cursor, containers, and CI. ${PRO_PRICE_LABEL}.\n Start Pro: ${checkoutUrl}\n`,
240
240
  ];
241
241
  // Rotate message daily — no Math.random (security policy)
242
242
  const msg = messages[Math.floor(Date.now() / 86400000) % messages.length];
@@ -921,7 +921,7 @@ function init(cliArgs = parseArgs(process.argv.slice(3))) {
921
921
  console.log('Scaffold ThumbGate in the current project and wire detected agent integrations.');
922
922
  console.log('');
923
923
  console.log('Options:');
924
- console.log(' --agent <name> Wire a specific agent: claude-code, codex, gemini, amp, cursor, cline');
924
+ console.log(' --agent <name> Wire a specific agent: claude-code, codex, gemini, amp, cursor, cline, opencode');
925
925
  console.log(' --wire-hooks Wire hooks only; do not scaffold project files');
926
926
  console.log(' --email <email> Subscribe installer to the setup guide and trial reminders');
927
927
  console.log(' --dry-run Show hook changes without writing them');
@@ -1593,6 +1593,7 @@ function pro() {
1593
1593
  trackEvent('cli_pro_view', { command: 'pro' });
1594
1594
  const args = parseArgs(process.argv.slice(3));
1595
1595
  const {
1596
+ notifyHostedProActivation,
1596
1597
  resolveProKey,
1597
1598
  saveLicense,
1598
1599
  startLocalProDashboard,
@@ -1606,9 +1607,10 @@ function pro() {
1606
1607
  console.log('Self-serve side lane today: Pro ($19/mo or $149/yr).');
1607
1608
  console.log('Every licensed Pro user gets a personal local dashboard on localhost.');
1608
1609
  console.log('\nWhat is available:');
1609
- console.log(' - Hosted sync: keep lessons, rules, and dashboard state aligned across laptops, CI, containers, and agent runtimes');
1610
+ console.log(' - Personal recall: search lessons, rules, and proof');
1610
1611
  console.log(' - Local Pro dashboard: your own browser dashboard for search, gates, and DPO export');
1611
- console.log(' - Team rollout path: shared hosted lessons, org visibility, and workflow proof');
1612
+ console.log(' - Managed adapters: Claude Code, Cursor, Codex, Gemini, Amp, Cline, OpenCode');
1613
+ console.log(' - Team rollout path: Enterprise adds shared hosted lessons, org visibility, workflow proof');
1612
1614
  console.log(' - Commercial truth doc: source of truth for traction, pricing, and proof claims');
1613
1615
  console.log('\nLinks:');
1614
1616
  console.log(` Buy Pro : ${PRO_CHECKOUT_URL}`);
@@ -1661,7 +1663,22 @@ function pro() {
1661
1663
  console.log('\n✅ Pro license activated!');
1662
1664
  console.log(` Key saved to: ${licensePath}`);
1663
1665
  console.log(' Launching your personal local dashboard...\n');
1664
- return launchDashboard(license.key, 'pro_activate');
1666
+ return notifyHostedProActivation({
1667
+ key: license.key,
1668
+ source: 'cli_pro_activate',
1669
+ version: license.version,
1670
+ })
1671
+ .then((notificationResult) => {
1672
+ appendLocalTelemetry({
1673
+ eventType: 'pro_activation_alert',
1674
+ version: license.version,
1675
+ timestamp: new Date().toISOString(),
1676
+ notified: Boolean(notificationResult && notificationResult.notified),
1677
+ sent: Boolean(notificationResult && notificationResult.alert && notificationResult.alert.sent),
1678
+ reason: notificationResult && notificationResult.reason ? notificationResult.reason : null,
1679
+ });
1680
+ return launchDashboard(license.key, 'pro_activate');
1681
+ });
1665
1682
  }
1666
1683
 
1667
1684
  if (args.upgrade) {
@@ -3934,6 +3951,7 @@ switch (COMMAND) {
3934
3951
  // PreToolUse hook interface: reads tool call JSON from stdin, outputs gate verdict
3935
3952
  // Used by: generate-pretool-hook.sh → npx thumbgate gate-check
3936
3953
  const { run: gateRun, runAsync: gateRunAsync } = require(path.join(PKG_ROOT, 'scripts', 'gates-engine'));
3954
+ const { evaluateSelfProtection } = require(path.join(PKG_ROOT, 'scripts', 'self-protection'));
3937
3955
  let stdinData = '';
3938
3956
  process.stdin.setEncoding('utf8');
3939
3957
  process.stdin.on('data', (chunk) => { stdinData += chunk; });
@@ -3941,7 +3959,32 @@ switch (COMMAND) {
3941
3959
  try {
3942
3960
  const input = JSON.parse(stdinData);
3943
3961
  const output = await gateRunAsync(input);
3944
- process.stdout.write(output + '\n');
3962
+ // Self-protection overlay (2026-07-08): the gate engine only denies edits
3963
+ // to ThumbGate's own governance files under strict enforcement; by default
3964
+ // such edits pass as a clean ALLOW. Surface them (or block in strict) so an
3965
+ // agent can't silently disable the firewall. Only overlays when the engine
3966
+ // did not already produce a hard deny.
3967
+ let verdict = output;
3968
+ try {
3969
+ const parsed = JSON.parse(output || '{}');
3970
+ const alreadyDeny = (parsed.hookSpecificOutput && parsed.hookSpecificOutput.permissionDecision === 'deny')
3971
+ || parsed.decision === 'block';
3972
+ if (!alreadyDeny) {
3973
+ const sp = evaluateSelfProtection(input.tool_name, input.tool_input);
3974
+ if (sp && sp.action === 'block') {
3975
+ verdict = JSON.stringify({
3976
+ decision: 'block',
3977
+ reason: sp.message,
3978
+ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: sp.message },
3979
+ });
3980
+ } else if (sp && sp.action === 'warn') {
3981
+ verdict = JSON.stringify({
3982
+ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: sp.message },
3983
+ });
3984
+ }
3985
+ }
3986
+ } catch (_e) { /* non-JSON engine output: pass through unchanged */ }
3987
+ process.stdout.write(verdict + '\n');
3945
3988
  process.exit(0);
3946
3989
  } catch (err) {
3947
3990
  process.stderr.write(`gate-check error: ${err.message}\n`);
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Insert 'dashboard' and '--open' as the subcommands/arguments
5
+ process.argv.splice(2, 0, 'dashboard', '--open');
6
+
7
+ require('./cli.js');
@@ -15,41 +15,32 @@ if (isCI || isQuiet) process.exit(0);
15
15
  const {
16
16
  PRO_MONTHLY_PAYMENT_LINK,
17
17
  PRO_PRICE_LABEL,
18
+ TEAM_PRICE_LABEL,
18
19
  } = require('../scripts/commercial-offer');
19
20
 
20
21
  // Tracked click-through path: /go/pro → /checkout/pro → Stripe.
22
+ // This captures UTM attribution in our funnel before handing off to Stripe.
21
23
  const PRO_CTA_URL = 'https://thumbgate.ai/go/pro?utm_source=npm&utm_medium=postinstall&utm_campaign=first_dollar';
24
+ const WORKFLOW_SPRINT_URL = 'https://thumbgate.ai/#workflow-sprint-intake';
22
25
  const DASHBOARD_URL = 'https://thumbgate.ai/dashboard?utm_source=npm&utm_medium=postinstall&utm_campaign=dashboard_nudge';
23
26
 
24
27
  process.stderr.write(`
25
28
  ╭─────────────────────────────────────────────────────╮
26
- │ ThumbGate installed.
29
+ │ ThumbGate installed — 7-day Pro trial is live.
27
30
  │ │
28
- Every repeat-mistake your agent makes costs
29
- tokens. ThumbGate blocks known-bad tool calls
30
- │ BEFORE the model sees them — zero tokens spent │
31
- │ on mistakes you've already corrected. │
31
+ Start now: npx thumbgate init
32
+ Updates: npx thumbgate subscribe you@company.com
32
33
  │ │
33
- Start free (local-only, never expires):
34
- npx thumbgate init
35
- │ npx thumbgate stats │
36
- │ │
37
- │ See your gates firing live: │
38
- │ ${DASHBOARD_URL.slice(0, 47).padEnd(47, ' ')} │
34
+ Free after trial: 3 rules, 2 captures/day.
35
+ Pro ($19/mo): unlimited everything.
39
36
  ╰─────────────────────────────────────────────────────╯
40
37
 
41
- Pro ${PRO_PRICE_LABEL}
42
- Hosted lesson sync across all your machines.
43
- Adapter matrix kept current for Claude Code,
44
- Cursor, Codex, Gemini, Amp, Cline, OpenCode.
45
- Org dashboard. DPO export. 24×7 ops.
46
- Upgrade: ${PRO_CTA_URL}
47
- Direct: ${PRO_MONTHLY_PAYMENT_LINK}
48
-
49
- ⚡ LAUNCH PROMO: 50% off first 3 months with code THUMB50
50
- One-click (discount applied): https://buy.stripe.com/eVqeVd62o0aO19v7mb3sI3g
51
- Limited to first 50 customers. July 2026
38
+ Trial unlocks: unlimited rules, lesson search, DPO export,
39
+ and personal dashboard proof. After 7 days, free tier limits apply.
40
+ Subscribe for the 5-min setup guide + weekly tips:
41
+ npx thumbgate subscribe you@company.com
52
42
 
53
- Or run: npx thumbgate pro
43
+ Dashboard: ${DASHBOARD_URL}
44
+ Keep Pro: ${PRO_CTA_URL}
54
45
 
55
46
  `);
@@ -0,0 +1,15 @@
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
+
@@ -0,0 +1,15 @@
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
+
@@ -0,0 +1,6 @@
1
+ {
2
+ "activeKid": "tgk_fd49d8ab2055",
3
+ "keys": {
4
+ "tgk_fd49d8ab2055": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEBVF8rLha9zsIhuiZ0CCslJK0SuCG6HL/bWXbkYVRiA=\n-----END PUBLIC KEY-----\n"
5
+ }
6
+ }
@@ -210,7 +210,7 @@
210
210
  {
211
211
  "id": "deny-network-egress",
212
212
  "layer": "Cloud",
213
- "pattern": "curl\\s|wget\\s|fetch\\(|https?://(?!github\\.com|registry\\.npmjs\\.org|api\\.anthropic\\.com)",
213
+ "pattern": "fetch\\(|https?://(?!(?:github\\.com|registry\\.npmjs\\.org|api\\.anthropic\\.com|localhost|127\\.0\\.0\\.1|\\[::1\\]|0\\.0\\.0\\.0)(?:[:/?#]|$))|(?:curl|wget)\\s+(?:-\\S+\\s+)*(?!https?://|(?:github\\.com|registry\\.npmjs\\.org|api\\.anthropic\\.com|localhost|127\\.0\\.0\\.1|\\[::1\\]|0\\.0\\.0\\.0)(?:[:/?#]|$))[\\w.-]+\\.[a-z]{2,}",
214
214
  "action": "warn",
215
215
  "unless": "egress_approved",
216
216
  "message": "Potential unauthorized network egress detected.",
@@ -2,8 +2,8 @@
2
2
  "repo": "IgorGanapolsky/ThumbGate",
3
3
  "repositoryUrl": "https://github.com/IgorGanapolsky/ThumbGate",
4
4
  "homepageUrl": "https://thumbgate.ai",
5
- "githubDescription": "Agent governance for ThumbGate: 👍/👎 become Pre-Action Checks that block repeat mistakes before code, money, or customer systems change.",
6
- "metaDescription": "Stripe Connect integration engineering for ticketing and marketplace platforms — Pretix plugin shipped to Hilltown Media. ThumbGate is also the pre-action governance runtime for AI coding agents (Claude Code, Cursor, Codex, Gemini CLI): 👍 thumbs up and 👎 thumbs down feedback become history-aware lessons, shared lessons and org visibility, and PreToolUse checks that block repeated mistakes before code, money, or customer systems change. Perfect for vibe coding and human-in-the-loop workflows.",
5
+ "githubDescription": "Agent governance: thumbs become Pre-Action Checks that flag repeat mistakes and hard-block catastrophic classes before code or systems change.",
6
+ "metaDescription": "ThumbGate is the pre-action governance runtime for AI coding agents (Claude Code, Cursor, Codex, Gemini CLI, Amp, Cline, OpenCode): 👍 thumbs up and 👎 thumbs down feedback become history-aware lessons stored locally; Enterprise adds shared lessons and org visibility. PreToolUse checks flag and log risky tool calls hard-blocking catastrophic classes by default and warning on the rest, with strict mode to hard-block every rule.",
7
7
  "topics": [
8
8
  "thumbgate",
9
9
  "pre-action-checks",
@@ -18,5 +18,8 @@
18
18
  "failingBuckets": [
19
19
  "fail",
20
20
  "cancel"
21
+ ],
22
+ "selfReferentialChecks": [
23
+ "Trunk Merge Queue (main)"
21
24
  ]
22
25
  }