thumbgate 1.28.3 → 1.28.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thumbgate",
3
3
  "description": "One 👎 becomes a hard rule the agent cannot bypass. Captures thumbs-down feedback, distills it into PreToolUse Pre-Action Checks, enforced across every future Claude Code session.",
4
- "version": "1.28.3",
4
+ "version": "1.28.4",
5
5
  "author": {
6
6
  "name": "Igor Ganapolsky",
7
7
  "email": "ig5973700@gmail.com",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.28.3",
3
+ "version": "1.28.4",
4
4
  "description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
5
5
  "homepage": "https://thumbgate.ai",
6
6
  "transport": "stdio",
@@ -2,13 +2,13 @@
2
2
  "mcpServers": {
3
3
  "thumbgate": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "thumbgate@1.28.3", "thumbgate", "serve"]
5
+ "args": ["--yes", "--package", "thumbgate@1.28.4", "thumbgate", "serve"]
6
6
  }
7
7
  },
8
8
  "hooks": {
9
9
  "preToolUse": {
10
10
  "command": "npx",
11
- "args": ["--yes", "--package", "thumbgate@1.28.3", "thumbgate", "gate-check"]
11
+ "args": ["--yes", "--package", "thumbgate@1.28.4", "thumbgate", "gate-check"]
12
12
  }
13
13
  }
14
14
  }
@@ -9,12 +9,12 @@ version: "1"
9
9
  skills:
10
10
  thumbgate-gate-check:
11
11
  description: "ThumbGate PreToolUse gate — blocks known-bad tool calls"
12
- command: "npx --yes --package thumbgate@0.9.13 thumbgate gate-check"
12
+ command: "npx --yes --package thumbgate@1.28.4 thumbgate gate-check"
13
13
  trigger: pre_tool_use
14
14
 
15
15
  thumbgate-feedback:
16
16
  description: "ThumbGate feedback capture — logs user prompt context"
17
- command: "npx --yes --package thumbgate@0.9.13 thumbgate hook-auto-capture"
17
+ command: "npx --yes --package thumbgate@1.28.4 thumbgate hook-auto-capture"
18
18
  trigger: user_prompt
19
19
 
20
20
  mcp:
@@ -23,6 +23,6 @@ mcp:
23
23
  args:
24
24
  - "--yes"
25
25
  - "--package"
26
- - "thumbgate@0.9.13"
26
+ - "thumbgate@1.28.4"
27
27
  - "thumbgate"
28
28
  - "serve"
@@ -305,7 +305,7 @@ const {
305
305
  finalizeSession: finalizeFeedbackSession,
306
306
  } = require('../../scripts/feedback-session');
307
307
 
308
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.28.3' };
308
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.28.4' };
309
309
  const COMMERCE_CATEGORIES = [
310
310
  'product_recommendation',
311
311
  'brand_compliance',
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.28.3",
10
+ "thumbgate@1.28.4",
11
11
  "thumbgate",
12
12
  "serve"
13
13
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.28.3",
3
+ "version": "1.28.4",
4
4
  "description": "ThumbGate Pre-Action Checks derive rules from repeated failures, flag risky tool calls, hard-block detected secret leaks, and block matches in strict mode.",
5
5
  "homepage": "https://thumbgate.ai",
6
6
  "repository": {
package/public/index.html CHANGED
@@ -20,7 +20,7 @@ __GOOGLE_SITE_VERIFICATION_META__
20
20
  <meta property="og:image" content="https://thumbgate.ai/og.png">
21
21
  <meta name="twitter:card" content="summary_large_image">
22
22
  <meta name="twitter:image" content="https://thumbgate.ai/og.png">
23
- <meta name="thumbgate-version" content="1.28.3">
23
+ <meta name="thumbgate-version" content="1.28.4">
24
24
  <meta name="keywords" content="ThumbGate, thumbgate, AI agent orchestration, AI experience orchestration, agentic development cycle, AC/DC framework, Guide Generate Verify Solve, agent enforcement layer, pre-action checks, agent governance, Claude Code, Cursor, Codex, Gemini, Amp, Cline, OpenCode, workflow hardening, context engineering, AI authenticity, brand authenticity AI">
25
25
  <link rel="canonical" href="__APP_ORIGIN__/">
26
26
  <link rel="alternate" type="text/markdown" title="ThumbGate LLM context" href="__APP_ORIGIN__/llm-context.md">
@@ -1776,7 +1776,7 @@ __GA_BOOTSTRAP__
1776
1776
  <a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
1777
1777
  <a href="/blog">Blog</a>
1778
1778
  </div>
1779
- <span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.28.3</span>
1779
+ <span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.28.4</span>
1780
1780
  </div>
1781
1781
  </footer>
1782
1782
 
@@ -25,7 +25,7 @@
25
25
  "alternateName": "thumbgate",
26
26
  "applicationCategory": "DeveloperApplication",
27
27
  "operatingSystem": "Cross-platform, Node.js >=18.18.0",
28
- "softwareVersion": "1.28.3",
28
+ "softwareVersion": "1.28.4",
29
29
  "url": "https://thumbgate.ai/numbers",
30
30
  "dateModified": "2026-05-07",
31
31
  "creator": {
@@ -202,7 +202,7 @@
202
202
  <main class="container">
203
203
  <h1>The Numbers</h1>
204
204
  <p class="subtitle">Generated first-party operational snapshot from the ThumbGate runtime. This is not customer traction, install volume, revenue, or proof that a configured gate has fired.</p>
205
- <div class="freshness">Updated: 2026-05-07 · Version 1.28.3</div>
205
+ <div class="freshness">Updated: 2026-05-07 · Version 1.28.4</div>
206
206
  <div class="truth-note"><strong>Read this first:</strong> configured checks are inventory. Recorded blocks and warnings are usage evidence. This snapshot currently reports 0 recorded hard-block event(s) and 0 recorded warning event(s).</div>
207
207
 
208
208
  <h2>Gate enforcement</h2>
@@ -118,7 +118,9 @@ function formatCliOutput(result) {
118
118
  }
119
119
  if (feedbackResult.promoted === false || !memoryId) {
120
120
  const clarification = feedbackResult.prompt || feedbackResult.message || feedbackResult.reason;
121
- lines.push(`${D} Reusable memory: not created${clarification ? ` — ${clarification}` : ''}${RST}`);
121
+ let reusableMemoryLine = `${D} Reusable memory: not created`;
122
+ if (clarification) reusableMemoryLine += ` — ${clarification}`;
123
+ lines.push(`${reusableMemoryLine}${RST}`);
122
124
  }
123
125
  } else {
124
126
  lines.push(`${R}Feedback not accepted: ${feedbackResult?.reason || 'unknown'}${RST}`);
@@ -94,7 +94,9 @@ function emitAnonymousFeedbackPing(signal) {
94
94
  return;
95
95
  }
96
96
 
97
- const normalizedSignal = signal === 'positive' ? 'up' : signal === 'negative' ? 'down' : null;
97
+ let normalizedSignal = null;
98
+ if (signal === 'positive') normalizedSignal = 'up';
99
+ if (signal === 'negative') normalizedSignal = 'down';
98
100
  if (!normalizedSignal) return;
99
101
 
100
102
  // Reuse the canonical installId from cli-telemetry.js (persisted at
@@ -105,24 +107,24 @@ function emitAnonymousFeedbackPing(signal) {
105
107
  try {
106
108
  const { getInstallId } = require('./cli-telemetry');
107
109
  installId = getInstallId();
108
- } catch (_) { /* fall through */ }
110
+ } catch {
111
+ installId = null;
112
+ }
109
113
  if (!installId) {
110
- try {
111
- installId = require('crypto').randomUUID();
112
- } catch (_) {
113
- return; // no crypto, no install id → drop silently
114
- }
114
+ installId = crypto.randomUUID();
115
115
  }
116
116
 
117
117
  let tier = 'free';
118
118
  try {
119
119
  const { getStatuslineMeta } = require('./statusline-meta');
120
120
  const meta = getStatuslineMeta({ env });
121
- const rawTier = String(meta && meta.tier ? meta.tier : 'free').toLowerCase();
121
+ const rawTier = String(meta?.tier || 'free').toLowerCase();
122
122
  if (rawTier === 'pro' || rawTier === 'enterprise' || rawTier === 'free') {
123
123
  tier = rawTier;
124
124
  }
125
- } catch (_) { /* default to 'free' */ }
125
+ } catch {
126
+ tier = 'free';
127
+ }
126
128
 
127
129
  const base = env.THUMBGATE_PUBLIC_APP_ORIGIN
128
130
  || env.THUMBGATE_API_URL
@@ -141,13 +143,17 @@ function emitAnonymousFeedbackPing(signal) {
141
143
  if (typeof fetch !== 'function' || typeof AbortSignal === 'undefined' || typeof AbortSignal.timeout !== 'function') {
142
144
  return;
143
145
  }
144
- fetch(`${base.replace(/\/+$/, '')}/v1/telemetry/ping`, {
146
+ let appOrigin = String(base);
147
+ while (appOrigin.endsWith('/')) appOrigin = appOrigin.slice(0, -1);
148
+ void fetch(`${appOrigin}/v1/telemetry/ping`, {
145
149
  method: 'POST',
146
150
  headers: { 'Content-Type': 'application/json' },
147
151
  body,
148
152
  signal: AbortSignal.timeout(2000),
149
- }).catch(() => { /* fire-and-forget */ });
150
- } catch (_) { /* telemetry must never disrupt CLI */ }
153
+ }).catch(() => undefined);
154
+ } catch {
155
+ return;
156
+ }
151
157
  }
152
158
 
153
159
  function isAuditTrailEntry(entry = {}) {
@@ -1793,7 +1799,7 @@ function captureFeedback(params) {
1793
1799
  // AGENTS.md/GEMINI.md merely because a negative rule was promoted.
1794
1800
  if (isSelfHarnessOptimizerEnabled()) {
1795
1801
  try {
1796
- const { fork } = require('child_process');
1802
+ const { fork } = require('node:child_process');
1797
1803
  const localOptimizerPath = path.join(process.cwd(), 'scripts', 'self-harness-optimizer.js');
1798
1804
  const packageOptimizerPath = path.join(__dirname, 'self-harness-optimizer.js');
1799
1805
 
@@ -1820,8 +1826,9 @@ function captureFeedback(params) {
1820
1826
  feedbackDir: FEEDBACK_DIR,
1821
1827
  outputDir: process.env.THUMBGATE_OBSIDIAN_VAULT_PATH,
1822
1828
  });
1823
- } catch (_err) {
1824
- // Non-critical, do not crash feedback loop
1829
+ } catch {
1830
+ resolve();
1831
+ return;
1825
1832
  }
1826
1833
  resolve();
1827
1834
  });
@@ -2511,6 +2518,7 @@ function buildCorrectiveActionsReminder(correctiveActions = []) {
2511
2518
 
2512
2519
  module.exports = {
2513
2520
  captureFeedback: captureFeedbackIdempotent,
2521
+ emitAnonymousFeedbackPing,
2514
2522
  buildFeedbackSourceIdentity,
2515
2523
  compactMemories,
2516
2524
  buildCorrectiveActionsReminder,
@@ -3,7 +3,7 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
- const { execFileSync } = require('child_process');
6
+ const { execFileSync } = require('node:child_process');
7
7
 
8
8
  const {
9
9
  DEFAULT_BASE_BRANCH,
@@ -90,7 +90,9 @@ function safeExecFileLines(binary, args, cwd) {
90
90
  }
91
91
 
92
92
  function normalizeGlob(glob) {
93
- return normalizePosix(glob).replace(/\/+$/, '');
93
+ let normalized = normalizePosix(glob);
94
+ while (normalized.endsWith('/')) normalized = normalized.slice(0, -1);
95
+ return normalized;
94
96
  }
95
97
 
96
98
  function sanitizeGlobList(globs) {