thumbgate 1.12.0 → 1.12.1
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/adapters/README.md +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/package.json +3 -3
- package/public/index.html +2 -2
- package/scripts/gates-engine.js +81 -2
- package/scripts/mcp-config.js +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate-marketplace",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"owner": {
|
|
5
5
|
"name": "Igor Ganapolsky",
|
|
6
6
|
"email": "ig5973700@gmail.com"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"source": "npm",
|
|
14
14
|
"package": "thumbgate"
|
|
15
15
|
},
|
|
16
|
-
"version": "1.12.
|
|
16
|
+
"version": "1.12.1",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Igor Ganapolsky"
|
|
19
19
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
3
|
"description": "Type 👍 or 👎 on any agent action. ThumbGate captures it, distills a lesson, and blocks the pattern from repeating. One thumbs-down = the agent physically cannot make that mistake again. 33 pre-action gates, budget enforcement, self-protection, and NIST/SOC2 compliance tags.",
|
|
4
|
-
"version": "1.12.
|
|
4
|
+
"version": "1.12.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
|
|
5
5
|
"homepage": "https://thumbgate-production.up.railway.app",
|
|
6
6
|
"transport": "stdio",
|
package/adapters/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- `chatgpt/openapi.yaml`: import into GPT Actions.
|
|
4
4
|
- `gemini/function-declarations.json`: Gemini function-calling definitions.
|
|
5
5
|
- `mcp/server-stdio.js`: underlying local MCP stdio server implementation.
|
|
6
|
-
- `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.12.
|
|
6
|
+
- `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.12.1 thumbgate serve`.
|
|
7
7
|
- `codex/config.toml`: example Codex MCP profile section using the same version-pinned portable launcher.
|
|
8
8
|
- `amp/skills/thumbgate-feedback/SKILL.md`: Amp skill template.
|
|
9
9
|
- `opencode/opencode.json`: portable OpenCode MCP profile using the same version-pinned portable launcher.
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"thumbgate": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--yes", "--package", "thumbgate@1.12.
|
|
5
|
+
"args": ["--yes", "--package", "thumbgate@1.12.1", "thumbgate", "serve"]
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
"hooks": {
|
|
9
9
|
"preToolUse": {
|
|
10
10
|
"command": "npx",
|
|
11
|
-
"args": ["--yes", "--package", "thumbgate@1.12.
|
|
11
|
+
"args": ["--yes", "--package", "thumbgate@1.12.1", "thumbgate", "gate-check"]
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -153,7 +153,7 @@ const {
|
|
|
153
153
|
finalizeSession: finalizeFeedbackSession,
|
|
154
154
|
} = require('../../scripts/feedback-session');
|
|
155
155
|
|
|
156
|
-
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.12.
|
|
156
|
+
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.12.1' };
|
|
157
157
|
const COMMERCE_CATEGORIES = [
|
|
158
158
|
'product_recommendation',
|
|
159
159
|
'brand_compliance',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "Self-improving agent governance: type thumbs-up or thumbs-down on any AI agent action. ThumbGate turns every mistake into a prevention rule and blocks the pattern from repeating. One thumbs-down, never again. 33 pre-action gates, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
|
|
5
5
|
"homepage": "https://thumbgate-production.up.railway.app",
|
|
6
6
|
"repository": {
|
|
@@ -566,7 +566,7 @@
|
|
|
566
566
|
"node": ">=18.18.0"
|
|
567
567
|
},
|
|
568
568
|
"dependencies": {
|
|
569
|
-
"@anthropic-ai/sdk": "^0.
|
|
569
|
+
"@anthropic-ai/sdk": "^0.90.0",
|
|
570
570
|
"@google/genai": "^1.49.0",
|
|
571
571
|
"@huggingface/transformers": "^4.0.1",
|
|
572
572
|
"@lancedb/lancedb": "^0.27.2",
|
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
"better-sqlite3": "^12.9.0",
|
|
575
575
|
"dotenv": "^17.4.2",
|
|
576
576
|
"playwright-core": "^1.59.1",
|
|
577
|
-
"stripe": "^22.0.
|
|
577
|
+
"stripe": "^22.0.2"
|
|
578
578
|
},
|
|
579
579
|
"overrides": {
|
|
580
580
|
"express@4.22.1": {
|
package/public/index.html
CHANGED
|
@@ -974,7 +974,7 @@ __GA_BOOTSTRAP__
|
|
|
974
974
|
<!-- HOW IT WORKS -->
|
|
975
975
|
<section class="how-it-works" id="how-it-works">
|
|
976
976
|
<div class="container">
|
|
977
|
-
<div class="section-label">New in v1.12.
|
|
977
|
+
<div class="section-label">New in v1.12.1</div>
|
|
978
978
|
<h2 class="section-title">Three steps to stop repeated AI failures</h2>
|
|
979
979
|
<div class="steps">
|
|
980
980
|
<div class="step">
|
|
@@ -1330,7 +1330,7 @@ __GA_BOOTSTRAP__
|
|
|
1330
1330
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
1331
1331
|
<a href="/blog">Blog</a>
|
|
1332
1332
|
</div>
|
|
1333
|
-
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.12.
|
|
1333
|
+
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.12.1</span>
|
|
1334
1334
|
</div>
|
|
1335
1335
|
</footer>
|
|
1336
1336
|
|
package/scripts/gates-engine.js
CHANGED
|
@@ -83,6 +83,7 @@ const DEFAULT_PROTECTED_FILE_GLOBS = [
|
|
|
83
83
|
];
|
|
84
84
|
const EDIT_LIKE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit']);
|
|
85
85
|
const HIGH_RISK_BASH_PATTERN = /\b(?:git\s+(?:add|commit|push)|gh\s+pr\s+(?:create|merge)|npm\s+publish|yarn\s+publish|pnpm\s+publish|rm\s+-rf)\b/i;
|
|
86
|
+
const REMOTE_SIDE_EFFECT_BASH_PATTERN = /\b(?:git\s+push\b|gh\s+pr\s+(?:create|merge|close|reopen|ready|edit)\b|gh\s+release\s+(?:create|delete|edit|upload)\b|npm\s+publish\b|yarn\s+publish\b|pnpm\s+publish\b)\b/i;
|
|
86
87
|
const BOOSTED_RISK_BLOCK_SCORE = 0.8;
|
|
87
88
|
const BOOSTED_RISK_MIN_EXAMPLES = 3;
|
|
88
89
|
const PR_THREAD_RESOLUTION_ACTION = 'pr_thread_resolution_verified_after_commit';
|
|
@@ -826,6 +827,59 @@ function evaluatePendingPrThreadResolutionGate(toolName, toolInput = {}) {
|
|
|
826
827
|
};
|
|
827
828
|
}
|
|
828
829
|
|
|
830
|
+
function getLocalOnlyScopeSources(governanceState = {}, constraints = {}) {
|
|
831
|
+
const sources = [];
|
|
832
|
+
if (governanceState.taskScope && governanceState.taskScope.localOnly) {
|
|
833
|
+
sources.push('task scope');
|
|
834
|
+
}
|
|
835
|
+
if (governanceState.branchGovernance && governanceState.branchGovernance.localOnly) {
|
|
836
|
+
sources.push('branch governance');
|
|
837
|
+
}
|
|
838
|
+
if (constraints.local_only && constraints.local_only.value === true) {
|
|
839
|
+
sources.push('local_only constraint');
|
|
840
|
+
}
|
|
841
|
+
return sources;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function isRemoteSideEffectCommand(toolName, toolInput = {}) {
|
|
845
|
+
if (toolName !== 'Bash') return false;
|
|
846
|
+
return REMOTE_SIDE_EFFECT_BASH_PATTERN.test(String(toolInput.command || ''));
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function evaluateLocalOnlyRemoteSideEffectGate(toolName, toolInput = {}, governanceState = {}, constraints = {}) {
|
|
850
|
+
if (!isRemoteSideEffectCommand(toolName, toolInput)) return null;
|
|
851
|
+
const sources = getLocalOnlyScopeSources(governanceState, constraints);
|
|
852
|
+
if (sources.length === 0) return null;
|
|
853
|
+
|
|
854
|
+
const command = String(toolInput.command || '').trim();
|
|
855
|
+
return {
|
|
856
|
+
decision: 'deny',
|
|
857
|
+
gate: 'local-only-remote-side-effect',
|
|
858
|
+
message: 'Task scope is local-only; remote git, PR, release, and publish actions are blocked until the local-only scope is cleared or explicitly changed.',
|
|
859
|
+
severity: 'critical',
|
|
860
|
+
reasoning: [
|
|
861
|
+
`Local-only source: ${sources.join(', ')}`,
|
|
862
|
+
`Blocked command: ${command.slice(0, 160)}`,
|
|
863
|
+
'Remote side effects are denied before configurable gates so wrapped commands cannot bypass local-only work boundaries',
|
|
864
|
+
],
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function recordStructuralGateBlock(toolName, toolInput, result) {
|
|
869
|
+
recordStat(result.gate, 'block');
|
|
870
|
+
const auditRecord = recordAuditEvent({
|
|
871
|
+
toolName,
|
|
872
|
+
toolInput,
|
|
873
|
+
decision: 'deny',
|
|
874
|
+
gateId: result.gate,
|
|
875
|
+
message: result.message,
|
|
876
|
+
severity: result.severity,
|
|
877
|
+
source: 'gates-engine',
|
|
878
|
+
});
|
|
879
|
+
auditToFeedback(auditRecord);
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
|
|
829
883
|
function isScopeEnforcedAction(toolName, toolInput = {}, affectedFiles = []) {
|
|
830
884
|
if (EDIT_LIKE_TOOLS.has(toolName) && affectedFiles.length > 0) return true;
|
|
831
885
|
if (toolName !== 'Bash') return false;
|
|
@@ -1333,7 +1387,18 @@ async function evaluateGatesAsync(toolName, toolInput, configPath) {
|
|
|
1333
1387
|
}
|
|
1334
1388
|
|
|
1335
1389
|
const constraints = loadConstraints();
|
|
1390
|
+
const governanceState = loadGovernanceState();
|
|
1336
1391
|
registerPrThreadResolutionClaimGate(toolName, toolInput);
|
|
1392
|
+
const localOnlyRemoteSideEffectGate = evaluateLocalOnlyRemoteSideEffectGate(
|
|
1393
|
+
toolName,
|
|
1394
|
+
toolInput,
|
|
1395
|
+
governanceState,
|
|
1396
|
+
constraints,
|
|
1397
|
+
);
|
|
1398
|
+
if (localOnlyRemoteSideEffectGate) {
|
|
1399
|
+
return recordStructuralGateBlock(toolName, toolInput, localOnlyRemoteSideEffectGate);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1337
1402
|
const pendingThreadResolutionGate = evaluatePendingPrThreadResolutionGate(toolName, toolInput);
|
|
1338
1403
|
if (pendingThreadResolutionGate) {
|
|
1339
1404
|
recordStat(pendingThreadResolutionGate.gate, 'block');
|
|
@@ -1445,7 +1510,7 @@ async function evaluateGatesAsync(toolName, toolInput, configPath) {
|
|
|
1445
1510
|
}
|
|
1446
1511
|
|
|
1447
1512
|
const sentinelReport = evaluateWorkflowSentinel(toolName, toolInput, {
|
|
1448
|
-
governanceState
|
|
1513
|
+
governanceState,
|
|
1449
1514
|
});
|
|
1450
1515
|
const sentinelDecision = recordSentinelDecision(sentinelReport, toolName, toolInput);
|
|
1451
1516
|
const memoryGuard = evaluateMemoryGuard(toolName, toolInput);
|
|
@@ -1503,7 +1568,18 @@ function evaluateGates(toolName, toolInput, configPath) {
|
|
|
1503
1568
|
}
|
|
1504
1569
|
|
|
1505
1570
|
const constraints = loadConstraints();
|
|
1571
|
+
const governanceState = loadGovernanceState();
|
|
1506
1572
|
registerPrThreadResolutionClaimGate(toolName, toolInput);
|
|
1573
|
+
const localOnlyRemoteSideEffectGate = evaluateLocalOnlyRemoteSideEffectGate(
|
|
1574
|
+
toolName,
|
|
1575
|
+
toolInput,
|
|
1576
|
+
governanceState,
|
|
1577
|
+
constraints,
|
|
1578
|
+
);
|
|
1579
|
+
if (localOnlyRemoteSideEffectGate) {
|
|
1580
|
+
return recordStructuralGateBlock(toolName, toolInput, localOnlyRemoteSideEffectGate);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1507
1583
|
const pendingThreadResolutionGate = evaluatePendingPrThreadResolutionGate(toolName, toolInput);
|
|
1508
1584
|
if (pendingThreadResolutionGate) {
|
|
1509
1585
|
recordStat(pendingThreadResolutionGate.gate, 'block');
|
|
@@ -1587,7 +1663,7 @@ function evaluateGates(toolName, toolInput, configPath) {
|
|
|
1587
1663
|
}
|
|
1588
1664
|
|
|
1589
1665
|
const sentinelReport = evaluateWorkflowSentinel(toolName, toolInput, {
|
|
1590
|
-
governanceState
|
|
1666
|
+
governanceState,
|
|
1591
1667
|
});
|
|
1592
1668
|
const sentinelDecision = recordSentinelDecision(sentinelReport, toolName, toolInput);
|
|
1593
1669
|
const memoryGuard = evaluateMemoryGuard(toolName, toolInput);
|
|
@@ -2250,6 +2326,9 @@ module.exports = {
|
|
|
2250
2326
|
evaluateBoostedRiskTagGuard,
|
|
2251
2327
|
registerPrThreadResolutionClaimGate,
|
|
2252
2328
|
evaluatePendingPrThreadResolutionGate,
|
|
2329
|
+
getLocalOnlyScopeSources,
|
|
2330
|
+
isRemoteSideEffectCommand,
|
|
2331
|
+
evaluateLocalOnlyRemoteSideEffectGate,
|
|
2253
2332
|
PR_THREAD_RESOLUTION_ACTION,
|
|
2254
2333
|
};
|
|
2255
2334
|
|
package/scripts/mcp-config.js
CHANGED
|
@@ -189,13 +189,13 @@ function publishedCliAvailable(pkgVersion) {
|
|
|
189
189
|
|
|
190
190
|
function resolveMcpEntry({ pkgRoot, pkgVersion, scope = 'project', targetDir = pkgRoot }) {
|
|
191
191
|
if (!isSourceCheckout(pkgRoot)) {
|
|
192
|
-
return
|
|
192
|
+
return codexAutoUpdateMcpEntry();
|
|
193
193
|
}
|
|
194
194
|
if (scope === 'home' && publishedCliAvailable(pkgVersion)) {
|
|
195
|
-
return
|
|
195
|
+
return codexAutoUpdateMcpEntry();
|
|
196
196
|
}
|
|
197
197
|
if (scope === 'project' && !isSameCheckoutFamily(pkgRoot, targetDir) && publishedCliAvailable(pkgVersion)) {
|
|
198
|
-
return
|
|
198
|
+
return codexAutoUpdateMcpEntry();
|
|
199
199
|
}
|
|
200
200
|
return localMcpEntry(pkgRoot, scope);
|
|
201
201
|
}
|