thumbgate 1.27.3 → 1.27.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.27.3",
4
+ "version": "1.27.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.27.3",
3
+ "version": "1.27.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.27.3", "thumbgate", "serve"]
5
+ "args": ["--yes", "--package", "thumbgate@1.27.4", "thumbgate", "serve"]
6
6
  }
7
7
  },
8
8
  "hooks": {
9
9
  "preToolUse": {
10
10
  "command": "npx",
11
- "args": ["--yes", "--package", "thumbgate@1.27.3", "thumbgate", "gate-check"]
11
+ "args": ["--yes", "--package", "thumbgate@1.27.4", "thumbgate", "gate-check"]
12
12
  }
13
13
  }
14
14
  }
@@ -231,7 +231,7 @@ const {
231
231
  finalizeSession: finalizeFeedbackSession,
232
232
  } = require('../../scripts/feedback-session');
233
233
 
234
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.27.3' };
234
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.27.4' };
235
235
  const COMMERCE_CATEGORIES = [
236
236
  'product_recommendation',
237
237
  'brand_compliance',
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.27.3",
10
+ "thumbgate@1.27.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.27.3",
3
+ "version": "1.27.4",
4
4
  "description": "ThumbGate self-improving agent governance: thumbs-up/down turns every mistake into a prevention rule and blocks repeat patterns. 36 pre-action checks, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
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.27.3">
23
+ <meta name="thumbgate-version" content="1.27.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, save LLM tokens, reduce Claude API cost, reduce OpenAI cost, AI agent token savings, prevent LLM retries, prevent hallucination retries, stop AI token waste, 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">
@@ -1676,7 +1676,7 @@ __GA_BOOTSTRAP__
1676
1676
  <a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
1677
1677
  <a href="/blog">Blog</a>
1678
1678
  </div>
1679
- <span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.27.3</span>
1679
+ <span class="footer-copy">© 2026 ThumbGate · MIT License · npm v1.27.4</span>
1680
1680
  </div>
1681
1681
  </footer>
1682
1682
 
@@ -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.27.3",
28
+ "softwareVersion": "1.27.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.27.3</div>
205
+ <div class="freshness">Updated: 2026-05-07 · Version 1.27.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>
@@ -449,9 +449,13 @@ function computeGateAuditSeries(feedbackDir, options = {}) {
449
449
  const dayKey = toLocalDayKey(entry.timestamp);
450
450
  if (!dayKey) continue;
451
451
  if (!countsByDay.has(dayKey)) {
452
- countsByDay.set(dayKey, { allow: 0, deny: 0, warn: 0 });
452
+ countsByDay.set(dayKey, { allow: 0, deny: 0, warn: 0, byGate: {} });
453
+ }
454
+ const bucket = countsByDay.get(dayKey);
455
+ bucket[entry.decision] += 1;
456
+ if ((entry.decision === 'deny' || entry.decision === 'warn') && entry.gateId) {
457
+ bucket.byGate[entry.gateId] = (bucket.byGate[entry.gateId] || 0) + 1;
453
458
  }
454
- countsByDay.get(dayKey)[entry.decision] += 1;
455
459
  }
456
460
 
457
461
  const days = [];
@@ -461,7 +465,7 @@ function computeGateAuditSeries(feedbackDir, options = {}) {
461
465
  const day = new Date(today);
462
466
  day.setDate(today.getDate() - offset);
463
467
  const dayKey = toLocalDayKey(day);
464
- const record = countsByDay.get(dayKey) || { allow: 0, deny: 0, warn: 0 };
468
+ const record = countsByDay.get(dayKey) || { allow: 0, deny: 0, warn: 0, byGate: {} };
465
469
  const intercepted = record.deny + record.warn;
466
470
  const total = intercepted + record.allow;
467
471
  const summary = {
@@ -471,6 +475,7 @@ function computeGateAuditSeries(feedbackDir, options = {}) {
471
475
  warn: record.warn,
472
476
  intercepted,
473
477
  total,
478
+ byGate: record.byGate || {},
474
479
  };
475
480
  totals.allow += record.allow;
476
481
  totals.deny += record.deny;
@@ -525,7 +530,14 @@ function computePreventionImpact(feedbackDir, gateStats) {
525
530
  // Last auto-promotion
526
531
  const autoGates = readJsonFile(autoGatesPath);
527
532
  let lastPromotion = null;
533
+ let promotionsToday = 0;
534
+ let promotionIdsToday = [];
528
535
  if (autoGates && Array.isArray(autoGates.promotionLog) && autoGates.promotionLog.length > 0) {
536
+ const todayKey = toLocalDayKey(new Date());
537
+ promotionIdsToday = autoGates.promotionLog
538
+ .filter((p) => p && p.timestamp && toLocalDayKey(p.timestamp) === todayKey)
539
+ .map((p) => p.gateId || p.id || 'unknown');
540
+ promotionsToday = promotionIdsToday.length;
529
541
  const sorted = autoGates.promotionLog
530
542
  .filter((p) => p.timestamp)
531
543
  .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
@@ -540,6 +552,8 @@ function computePreventionImpact(feedbackDir, gateStats) {
540
552
  estimatedHoursSaved,
541
553
  ruleCount,
542
554
  lastPromotion,
555
+ promotionsToday,
556
+ promotionIdsToday: promotionIdsToday.slice(0, 5),
543
557
  };
544
558
  }
545
559
 
package/src/api/server.js CHANGED
@@ -1567,7 +1567,69 @@ function compactNumber(value) {
1567
1567
  return Number.isFinite(n) ? n : 0;
1568
1568
  }
1569
1569
 
1570
- function buildEnterpriseChatSection(topic, dashboardData, status) {
1570
+ function isTodayScopedPrompt(prompt) {
1571
+ return /\btoday\b|\bthis day\b|\blast 24\b|\b24 hours\b/i.test(String(prompt || ''));
1572
+ }
1573
+
1574
+ function getTodayGateAudit(gateAudit) {
1575
+ const days = gateAudit && Array.isArray(gateAudit.days) ? gateAudit.days : [];
1576
+ return days.length > 0 ? days[days.length - 1] : null;
1577
+ }
1578
+
1579
+ function formatGateBuckets(byGate) {
1580
+ return Object.entries(byGate || {})
1581
+ .filter(([, count]) => compactNumber(count) > 0)
1582
+ .sort((a, b) => compactNumber(b[1]) - compactNumber(a[1]))
1583
+ .slice(0, 3)
1584
+ .map(([gateId, count]) => `${gateId} (${compactNumber(count)}x)`);
1585
+ }
1586
+
1587
+ function buildTodayGateAnswer(prompt, dashboardData, gateStats, gates) {
1588
+ if (!isTodayScopedPrompt(prompt)) return null;
1589
+ const lower = String(prompt || '').toLowerCase();
1590
+ const gateAudit = dashboardData.gateAudit || {};
1591
+ const prevention = dashboardData.prevention || {};
1592
+ const today = getTodayGateAudit(gateAudit) || { deny: 0, warn: 0, intercepted: 0, byGate: {} };
1593
+ const activeGateCount = gates.length || compactNumber(gateStats.totalGates);
1594
+
1595
+ if (/\b(activated|promoted|created|enabled)\b/.test(lower)) {
1596
+ const promotionsToday = compactNumber(prevention.promotionsToday);
1597
+ const promotedIds = Array.isArray(prevention.promotionIdsToday) ? prevention.promotionIdsToday.filter(Boolean) : [];
1598
+ return [
1599
+ `Gates activated today: ${promotionsToday}.`,
1600
+ `Active gates now: ${activeGateCount}.`,
1601
+ promotedIds.length > 0 ? `Promoted today: ${promotedIds.slice(0, 3).join(', ')}.` : '',
1602
+ ].filter(Boolean);
1603
+ }
1604
+
1605
+ if (/\b(what|which)\b/.test(lower) && /\b(mistake|mistakes|block|blocked|prevent|prevented)\b/.test(lower)) {
1606
+ const gateBuckets = formatGateBuckets(today.byGate);
1607
+ return [
1608
+ `Today: ${compactNumber(today.deny)} blocked actions and ${compactNumber(today.warn)} warning checkpoints.`,
1609
+ gateBuckets.length > 0
1610
+ ? `Top blocked/warned gates today: ${gateBuckets.join(', ')}.`
1611
+ : 'No per-gate blocked mistake names are present in today\'s local audit snapshot.',
1612
+ ];
1613
+ }
1614
+
1615
+ if (/\b(prevent|prevented|intercept|intercepted)\b/.test(lower)) {
1616
+ return [
1617
+ `Mistakes prevented today: ${compactNumber(today.intercepted)} interventions (${compactNumber(today.deny)} blocked, ${compactNumber(today.warn)} warned).`,
1618
+ `All-time blocked actions recorded: ${compactNumber(gateStats.blocked || gateStats.denied || gateStats.totalBlocked)}.`,
1619
+ ];
1620
+ }
1621
+
1622
+ if (/\b(block|blocked|deny|denied)\b/.test(lower)) {
1623
+ return [
1624
+ `Mistakes blocked today: ${compactNumber(today.deny)} deny decisions.`,
1625
+ `Warnings today: ${compactNumber(today.warn)}; all-time blocked actions recorded: ${compactNumber(gateStats.blocked || gateStats.denied || gateStats.totalBlocked)}.`,
1626
+ ];
1627
+ }
1628
+
1629
+ return null;
1630
+ }
1631
+
1632
+ function buildEnterpriseChatSection(topic, dashboardData, status, prompt) {
1571
1633
  const approval = dashboardData.approval || {};
1572
1634
  const gates = Array.isArray(dashboardData.gates) ? dashboardData.gates : [];
1573
1635
  const gateStats = dashboardData.gateStats || {};
@@ -1585,6 +1647,13 @@ function buildEnterpriseChatSection(topic, dashboardData, status) {
1585
1647
  };
1586
1648
  }
1587
1649
  if (topic === 'gates') {
1650
+ const todayGateAnswer = buildTodayGateAnswer(prompt, dashboardData, gateStats, gates);
1651
+ if (todayGateAnswer) {
1652
+ return {
1653
+ lines: todayGateAnswer,
1654
+ sources: ['gate audit', 'gate stats'],
1655
+ };
1656
+ }
1588
1657
  return {
1589
1658
  lines: [
1590
1659
  `Active gates: ${gates.length || compactNumber(gateStats.totalGates)}.`,
@@ -1635,7 +1704,7 @@ function buildEnterpriseChatSection(topic, dashboardData, status) {
1635
1704
 
1636
1705
  function buildEnterpriseChatAnswer(prompt, dashboardData, status) {
1637
1706
  const topic = classifyEnterpriseChatTopic(prompt);
1638
- const section = buildEnterpriseChatSection(topic, dashboardData, status);
1707
+ const section = buildEnterpriseChatSection(topic, dashboardData, status, prompt);
1639
1708
 
1640
1709
  return {
1641
1710
  topic,