thumbgate 0.9.12 → 0.9.14
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +5 -3
- package/adapters/README.md +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +35 -0
- package/package.json +2 -2
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-codex-bridge/.mcp.json +1 -1
- package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-profile/.mcp.json +1 -1
- package/plugins/codex-profile/INSTALL.md +1 -1
- package/plugins/codex-profile/README.md +1 -1
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
- package/plugins/opencode-profile/INSTALL.md +1 -1
- package/public/index.html +27 -3
- package/public/learn.html +61 -0
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/feedback-history-distiller.js +7 -1
- package/scripts/feedback-loop.js +10 -4
- package/scripts/feedback-paths.js +142 -10
- package/scripts/feedback-root-consolidator.js +18 -4
- package/scripts/post-everywhere.js +10 -0
- package/scripts/seo-gsd.js +217 -4
- package/scripts/statusline-cache-path.js +9 -6
- package/src/api/server.js +118 -27
|
@@ -43,14 +43,139 @@ function dirExists(dirPath) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function getHomeDir(options = {}) {
|
|
47
|
+
const env = options.env || process.env;
|
|
48
|
+
return options.home || env.HOME || env.USERPROFILE || HOME;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeDir(dirPath) {
|
|
52
|
+
if (!dirPath) return null;
|
|
53
|
+
try {
|
|
54
|
+
return path.resolve(String(dirPath));
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isWithinDir(candidate, parent) {
|
|
61
|
+
const normalizedCandidate = normalizeDir(candidate);
|
|
62
|
+
const normalizedParent = normalizeDir(parent);
|
|
63
|
+
if (!normalizedCandidate || !normalizedParent) return false;
|
|
64
|
+
const relative = path.relative(normalizedParent, normalizedCandidate);
|
|
65
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getRuntimeDir(options = {}) {
|
|
69
|
+
return path.join(getHomeDir(options), '.thumbgate', 'runtime');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getActiveProjectStatePath(options = {}) {
|
|
73
|
+
return path.join(getRuntimeDir(options), 'active-project.json');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isTransientProjectDir(dirPath, options = {}) {
|
|
77
|
+
const normalizedDir = normalizeDir(dirPath);
|
|
78
|
+
if (!normalizedDir) return true;
|
|
79
|
+
if (!dirExists(normalizedDir)) return true;
|
|
80
|
+
|
|
81
|
+
const runtimeDir = getRuntimeDir(options);
|
|
82
|
+
if (isWithinDir(normalizedDir, runtimeDir)) return true;
|
|
83
|
+
|
|
84
|
+
return normalizedDir.includes(`${path.sep}.npm${path.sep}_npx${path.sep}`)
|
|
85
|
+
|| /thumbgate-published-cli-/i.test(normalizedDir);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readActiveProjectState(options = {}) {
|
|
89
|
+
const statePath = getActiveProjectStatePath(options);
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
|
92
|
+
if (!parsed || !parsed.projectDir) return null;
|
|
93
|
+
if (isTransientProjectDir(parsed.projectDir, options)) return null;
|
|
94
|
+
return {
|
|
95
|
+
...parsed,
|
|
96
|
+
projectDir: normalizeDir(parsed.projectDir),
|
|
97
|
+
};
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function writeActiveProjectState(projectDir, options = {}) {
|
|
104
|
+
const normalizedDir = normalizeDir(projectDir);
|
|
105
|
+
if (isTransientProjectDir(normalizedDir, options)) return null;
|
|
106
|
+
|
|
107
|
+
const payload = {
|
|
108
|
+
projectDir: normalizedDir,
|
|
109
|
+
projectName: path.basename(normalizedDir) || 'default',
|
|
110
|
+
updatedAt: new Date().toISOString(),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const statePath = getActiveProjectStatePath(options);
|
|
114
|
+
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
115
|
+
fs.writeFileSync(statePath, JSON.stringify(payload, null, 2));
|
|
116
|
+
return payload;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveProjectDir(options = {}) {
|
|
120
|
+
const env = options.env || process.env;
|
|
121
|
+
const stored = options.includeStored === false ? null : readActiveProjectState(options);
|
|
122
|
+
const cwdCandidates = uniquePaths([
|
|
123
|
+
options.cwd,
|
|
124
|
+
env.PWD,
|
|
125
|
+
process.cwd(),
|
|
126
|
+
]);
|
|
127
|
+
const isTransientExecution = cwdCandidates.length > 0
|
|
128
|
+
&& cwdCandidates.every((candidate) => isTransientProjectDir(candidate, options));
|
|
129
|
+
const candidates = uniquePaths([
|
|
130
|
+
options.projectDir,
|
|
131
|
+
env.THUMBGATE_PROJECT_DIR,
|
|
132
|
+
env.CLAUDE_PROJECT_DIR,
|
|
133
|
+
isTransientExecution && stored && stored.projectDir,
|
|
134
|
+
env.INIT_CWD,
|
|
135
|
+
...cwdCandidates,
|
|
136
|
+
!isTransientExecution && stored && stored.projectDir,
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
for (const candidate of candidates) {
|
|
140
|
+
if (!candidate) continue;
|
|
141
|
+
if (!isTransientProjectDir(candidate, options)) {
|
|
142
|
+
return normalizeDir(candidate);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return normalizeDir(options.cwd || env.PWD || PROJECT_ROOT) || PROJECT_ROOT;
|
|
147
|
+
}
|
|
148
|
+
|
|
46
149
|
function getProjectName(cwd = process.cwd()) {
|
|
47
150
|
return path.basename(cwd || PROJECT_ROOT) || 'default';
|
|
48
151
|
}
|
|
49
152
|
|
|
153
|
+
function hasDirectProjectScope(options = {}) {
|
|
154
|
+
const env = options.env || process.env;
|
|
155
|
+
return Boolean(
|
|
156
|
+
options.explicitProjectDir
|
|
157
|
+
|| env.THUMBGATE_PROJECT_DIR
|
|
158
|
+
|| env.CLAUDE_PROJECT_DIR
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function hasExplicitProjectScope(options = {}) {
|
|
163
|
+
return Boolean(hasDirectProjectScope(options) || readActiveProjectState(options));
|
|
164
|
+
}
|
|
165
|
+
|
|
50
166
|
function getExplicitFeedbackDir(options = {}) {
|
|
51
167
|
const env = options.env || process.env;
|
|
52
168
|
if (options.feedbackDir) return options.feedbackDir;
|
|
53
|
-
if (
|
|
169
|
+
if (options.skipExplicitFeedbackDir) return null;
|
|
170
|
+
// A caller-provided feedback root should stay authoritative over stored
|
|
171
|
+
// active-project state so isolated CLI/test commands do not drift into a
|
|
172
|
+
// different project. Only direct project overrides suppress it.
|
|
173
|
+
if (env.THUMBGATE_FEEDBACK_DIR && !hasDirectProjectScope(options)) {
|
|
174
|
+
return env.THUMBGATE_FEEDBACK_DIR;
|
|
175
|
+
}
|
|
176
|
+
if (hasDirectProjectScope(options)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
54
179
|
if (env.RAILWAY_VOLUME_MOUNT_PATH) {
|
|
55
180
|
return path.join(env.RAILWAY_VOLUME_MOUNT_PATH, 'feedback');
|
|
56
181
|
}
|
|
@@ -58,30 +183,29 @@ function getExplicitFeedbackDir(options = {}) {
|
|
|
58
183
|
}
|
|
59
184
|
|
|
60
185
|
function getThumbgateFeedbackDir(options = {}) {
|
|
61
|
-
const
|
|
62
|
-
return path.join(
|
|
186
|
+
const projectDir = resolveProjectDir(options);
|
|
187
|
+
return path.join(projectDir, '.thumbgate');
|
|
63
188
|
}
|
|
64
189
|
|
|
65
190
|
function getFallbackFeedbackDir(options = {}) {
|
|
66
191
|
const env = options.env || process.env;
|
|
67
192
|
if (env._TEST_THUMBGATE_FALLBACK_FEEDBACK_DIR) return env._TEST_THUMBGATE_FALLBACK_FEEDBACK_DIR;
|
|
68
193
|
if (env.THUMBGATE_FALLBACK_FEEDBACK_DIR) return env.THUMBGATE_FALLBACK_FEEDBACK_DIR;
|
|
69
|
-
const
|
|
70
|
-
return path.join(
|
|
194
|
+
const projectDir = resolveProjectDir(options);
|
|
195
|
+
return path.join(projectDir, '.thumbgate-compat');
|
|
71
196
|
}
|
|
72
197
|
|
|
73
198
|
function getLegacyFeedbackDir(options = {}) {
|
|
74
199
|
const env = options.env || process.env;
|
|
75
200
|
if (env._TEST_LEGACY_FEEDBACK_DIR) return env._TEST_LEGACY_FEEDBACK_DIR;
|
|
76
201
|
if (env.THUMBGATE_LEGACY_FEEDBACK_DIR) return env.THUMBGATE_LEGACY_FEEDBACK_DIR;
|
|
77
|
-
const
|
|
78
|
-
return path.join(
|
|
202
|
+
const projectDir = resolveProjectDir(options);
|
|
203
|
+
return path.join(projectDir, '.claude', 'memory', 'feedback');
|
|
79
204
|
}
|
|
80
205
|
|
|
81
206
|
function getGlobalFeedbackDir(options = {}) {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
return path.join(home, '.thumbgate', 'projects', getProjectName(cwd));
|
|
207
|
+
const projectDir = resolveProjectDir(options);
|
|
208
|
+
return path.join(getHomeDir(options), '.thumbgate', 'projects', getProjectName(projectDir));
|
|
85
209
|
}
|
|
86
210
|
|
|
87
211
|
function resolveFeedbackDir(options = {}) {
|
|
@@ -133,13 +257,21 @@ module.exports = {
|
|
|
133
257
|
PROJECT_ROOT,
|
|
134
258
|
HOME,
|
|
135
259
|
buildFeedbackPathsFromDir,
|
|
260
|
+
getActiveProjectStatePath,
|
|
136
261
|
getFeedbackPaths,
|
|
137
262
|
getGlobalFeedbackDir,
|
|
263
|
+
getHomeDir,
|
|
138
264
|
getLegacyFeedbackDir,
|
|
139
265
|
getFallbackFeedbackDir,
|
|
266
|
+
getRuntimeDir,
|
|
140
267
|
getThumbgateFeedbackDir,
|
|
268
|
+
hasDirectProjectScope,
|
|
269
|
+
hasExplicitProjectScope,
|
|
270
|
+
readActiveProjectState,
|
|
141
271
|
listFallbackFeedbackDirs,
|
|
142
272
|
listFeedbackArtifactPaths,
|
|
273
|
+
resolveProjectDir,
|
|
143
274
|
resolveFallbackArtifactPath,
|
|
144
275
|
resolveFeedbackDir,
|
|
276
|
+
writeActiveProjectState,
|
|
145
277
|
};
|
|
@@ -29,6 +29,19 @@ const CONSOLIDATED_ARTIFACTS = [
|
|
|
29
29
|
'workflow-sprint-leads.jsonl',
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
+
function getScopedProjectOptions(options = {}) {
|
|
33
|
+
if (options.feedbackDir) return options;
|
|
34
|
+
|
|
35
|
+
const projectDir = options.projectDir || options.cwd;
|
|
36
|
+
if (!projectDir) return options;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...options,
|
|
40
|
+
projectDir,
|
|
41
|
+
explicitProjectDir: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
function ensureParentDir(filePath) {
|
|
33
46
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
34
47
|
}
|
|
@@ -194,11 +207,11 @@ function consolidateArtifact(fileName, options = {}) {
|
|
|
194
207
|
}
|
|
195
208
|
|
|
196
209
|
function consolidateFeedbackRoot(options = {}) {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|| getThumbgateFeedbackDir(
|
|
210
|
+
const scopedOptions = getScopedProjectOptions(options);
|
|
211
|
+
const feedbackDir = scopedOptions.feedbackDir
|
|
212
|
+
|| getThumbgateFeedbackDir(scopedOptions);
|
|
200
213
|
const artifacts = CONSOLIDATED_ARTIFACTS.map((fileName) => consolidateArtifact(fileName, {
|
|
201
|
-
...
|
|
214
|
+
...scopedOptions,
|
|
202
215
|
feedbackDir,
|
|
203
216
|
}));
|
|
204
217
|
const sourceRoots = Array.from(new Set(
|
|
@@ -229,6 +242,7 @@ module.exports = {
|
|
|
229
242
|
consolidateArtifact,
|
|
230
243
|
consolidateFeedbackRoot,
|
|
231
244
|
dedupeJsonlRows,
|
|
245
|
+
getScopedProjectOptions,
|
|
232
246
|
mergeCheckoutSessionsPayloads,
|
|
233
247
|
mergeKeyStorePayloads,
|
|
234
248
|
};
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
const fs = require('fs');
|
|
26
26
|
const path = require('path');
|
|
27
27
|
const { tagUrlsInText } = require('./social-analytics/utm');
|
|
28
|
+
const { isDuplicate, recordPost } = require('./social-analytics/publishers/zernio');
|
|
28
29
|
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
31
|
// Publisher imports (lazy — only loaded when needed)
|
|
@@ -259,9 +260,18 @@ async function postEverywhere(filePath, { platforms, dryRun } = {}) {
|
|
|
259
260
|
continue;
|
|
260
261
|
}
|
|
261
262
|
|
|
263
|
+
// Dedup guard: skip platforms where identical content was posted in last 24h
|
|
264
|
+
const dedupContent = [parsed.title, parsed.body].filter(Boolean).join('\n');
|
|
265
|
+
if (!dryRun && isDuplicate(dedupContent, platform)) {
|
|
266
|
+
console.log(`[post-everywhere] ${platform}: SKIPPED — duplicate content within 24h`);
|
|
267
|
+
results[platform] = { skipped: true, reason: 'duplicate_content_24h' };
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
262
271
|
try {
|
|
263
272
|
console.log(`\n[post-everywhere] Posting to ${platform}...`);
|
|
264
273
|
results[platform] = await dispatcher(parsed, dryRun);
|
|
274
|
+
if (!dryRun) recordPost(dedupContent, platform);
|
|
265
275
|
console.log(`[post-everywhere] ${platform}: OK`);
|
|
266
276
|
} catch (err) {
|
|
267
277
|
console.error(`[post-everywhere] ${platform}: FAILED — ${err.message}`);
|
package/scripts/seo-gsd.js
CHANGED
|
@@ -67,12 +67,30 @@ const HIGH_ROI_QUERY_SEEDS = [
|
|
|
67
67
|
source: 'seed',
|
|
68
68
|
notes: 'Problem-led copy that maps to landing-page positioning.',
|
|
69
69
|
},
|
|
70
|
+
{
|
|
71
|
+
query: 'cursor prevent repeated mistakes',
|
|
72
|
+
businessValue: 87,
|
|
73
|
+
source: 'seed',
|
|
74
|
+
notes: 'High-intent Cursor workflow page for developers already feeling repeat-failure pain.',
|
|
75
|
+
},
|
|
70
76
|
{
|
|
71
77
|
query: 'claude code prevent repeated mistakes',
|
|
72
78
|
businessValue: 86,
|
|
73
79
|
source: 'seed',
|
|
74
80
|
notes: 'High-intent pain query for Claude Code buyers.',
|
|
75
81
|
},
|
|
82
|
+
{
|
|
83
|
+
query: 'codex cli guardrails',
|
|
84
|
+
businessValue: 84,
|
|
85
|
+
source: 'seed',
|
|
86
|
+
notes: 'Guardrail-focused page for Codex CLI buyers who want prevention, not just memory.',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
query: 'gemini cli feedback memory',
|
|
90
|
+
businessValue: 82,
|
|
91
|
+
source: 'seed',
|
|
92
|
+
notes: 'Integration page for Gemini CLI users who need memory plus enforcement.',
|
|
93
|
+
},
|
|
76
94
|
];
|
|
77
95
|
|
|
78
96
|
const PAGE_BLUEPRINTS = [
|
|
@@ -225,6 +243,55 @@ const PAGE_BLUEPRINTS = [
|
|
|
225
243
|
],
|
|
226
244
|
relatedPaths: ['/compare/speclock', '/guides/claude-code-feedback'],
|
|
227
245
|
},
|
|
246
|
+
{
|
|
247
|
+
query: 'stop ai coding agents from repeating mistakes',
|
|
248
|
+
path: '/guides/stop-repeated-ai-agent-mistakes',
|
|
249
|
+
pageType: 'guide',
|
|
250
|
+
pillar: 'pre-action-gates',
|
|
251
|
+
title: 'How to Stop AI Coding Agents From Repeating Mistakes | ThumbGate',
|
|
252
|
+
heroTitle: 'How to Stop AI Coding Agents From Repeating Mistakes',
|
|
253
|
+
heroSummary: 'If your agent keeps repeating the same bad move, the fix is not more memory alone. The fix is a feedback loop that turns repeated failures into pre-action gates before the next tool call executes.',
|
|
254
|
+
takeaways: [
|
|
255
|
+
'Repeated mistakes are a workflow problem, not just a context-window problem.',
|
|
256
|
+
'ThumbGate turns thumbs-down feedback into prevention rules and runtime gates.',
|
|
257
|
+
'This page is meant to move problem-aware buyers into the Pro path or a concrete install.',
|
|
258
|
+
],
|
|
259
|
+
sections: [
|
|
260
|
+
{
|
|
261
|
+
heading: 'Why repeated mistakes keep happening',
|
|
262
|
+
paragraphs: [
|
|
263
|
+
'AI coding agents are fast, but they forget operational pain surprisingly easily. One bad deployment, force-push, or skipped verification step often turns into another because the system remembered the transcript but never enforced the lesson.',
|
|
264
|
+
'That is why teams feel stuck in a correction loop. They keep teaching the same rule, but the next session still allows the same risky action.',
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
heading: 'What changes when feedback becomes enforcement',
|
|
269
|
+
bullets: [
|
|
270
|
+
'Thumbs down captures the exact failure you do not want repeated.',
|
|
271
|
+
'Repeated failures promote into linked prevention rules.',
|
|
272
|
+
'Pre-action gates intercept the risky tool call before execution.',
|
|
273
|
+
'Thumbs up reinforces the safe path so the agent learns what good looks like too.',
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
heading: 'What a buyer should do next',
|
|
278
|
+
paragraphs: [
|
|
279
|
+
'If the pain is already real, do not start with a long architecture project. Start by wiring ThumbGate into the workflow where the agent has already burned time or trust, then watch the next repeat attempt get blocked before damage lands.',
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
faq: [
|
|
284
|
+
{
|
|
285
|
+
question: 'Is memory alone enough to stop repeated mistakes?',
|
|
286
|
+
answer: 'Usually no. Memory helps retrieval, but ThumbGate adds pre-action gates so the same risky move can be blocked before the next command executes.',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
question: 'Does ThumbGate only punish bad behavior?',
|
|
290
|
+
answer: 'No. Thumbs up reinforces good behavior, so the loop captures safe patterns as well as failures.',
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
relatedPaths: ['/guides/pre-action-gates', '/guides/claude-code-feedback'],
|
|
294
|
+
},
|
|
228
295
|
{
|
|
229
296
|
query: 'claude code feedback memory',
|
|
230
297
|
path: '/guides/claude-code-feedback',
|
|
@@ -273,6 +340,150 @@ const PAGE_BLUEPRINTS = [
|
|
|
273
340
|
],
|
|
274
341
|
relatedPaths: ['/guides/pre-action-gates', '/compare/mem0'],
|
|
275
342
|
},
|
|
343
|
+
{
|
|
344
|
+
query: 'cursor prevent repeated mistakes',
|
|
345
|
+
path: '/guides/cursor-agent-guardrails',
|
|
346
|
+
pageType: 'integration',
|
|
347
|
+
pillar: 'agent-workflows',
|
|
348
|
+
title: 'Cursor Agent Guardrails | Stop Repeated Mistakes with ThumbGate',
|
|
349
|
+
heroTitle: 'Cursor Guardrails That Block Repeated Mistakes',
|
|
350
|
+
heroSummary: 'Cursor moves fast, which makes repeated mistakes expensive. ThumbGate gives Cursor users a feedback loop that turns thumbs-down corrections into pre-action gates before the next risky step fires.',
|
|
351
|
+
takeaways: [
|
|
352
|
+
'Cursor users want speed without trusting the agent blindly.',
|
|
353
|
+
'ThumbGate adds enforcement without forcing a platform switch.',
|
|
354
|
+
'The page should answer the buyer question in one line: how do I stop Cursor from doing the same bad thing again?',
|
|
355
|
+
],
|
|
356
|
+
sections: [
|
|
357
|
+
{
|
|
358
|
+
heading: 'The Cursor workflow problem',
|
|
359
|
+
paragraphs: [
|
|
360
|
+
'Cursor can move from idea to edits quickly, but the failure mode is familiar: the same wrong refactor, risky shell command, or skipped check comes back in the next session because nothing hardened the workflow.',
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
heading: 'How ThumbGate fits into Cursor',
|
|
365
|
+
bullets: [
|
|
366
|
+
'Capture thumbs-up/down feedback on agent behavior.',
|
|
367
|
+
'Promote repeated failures into prevention rules.',
|
|
368
|
+
'Block known-bad commands with pre-action gates before execution.',
|
|
369
|
+
'Keep the memory and gates local-first so the operator retains control.',
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
heading: 'What makes this different from a rule file',
|
|
374
|
+
paragraphs: [
|
|
375
|
+
'Static rules help on day one. ThumbGate helps on day two and day twenty because it keeps learning from live corrections instead of relying on a fixed checklist that drifts out of date.',
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
faq: [
|
|
380
|
+
{
|
|
381
|
+
question: 'Do I need to leave Cursor to use ThumbGate?',
|
|
382
|
+
answer: 'No. ThumbGate is designed to sit alongside existing coding-agent workflows so you can add enforcement without switching tools.',
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
question: 'What kind of mistakes can Cursor guardrails stop?',
|
|
386
|
+
answer: 'Repeated failures like risky git actions, destructive scripts, skipped verification, or any other known-bad pattern you have already corrected once.',
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
relatedPaths: ['/guides/stop-repeated-ai-agent-mistakes', '/guides/pre-action-gates'],
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
query: 'codex cli guardrails',
|
|
393
|
+
path: '/guides/codex-cli-guardrails',
|
|
394
|
+
pageType: 'integration',
|
|
395
|
+
pillar: 'agent-workflows',
|
|
396
|
+
title: 'Codex CLI Guardrails | Prevent Repeated Mistakes with ThumbGate',
|
|
397
|
+
heroTitle: 'Codex CLI Guardrails That Actually Enforce',
|
|
398
|
+
heroSummary: 'Codex CLI can move quickly through repo tasks, but buyers need more than good intentions. ThumbGate adds a reliability gateway so repeated mistakes become searchable lessons, linked rules, and pre-action enforcement.',
|
|
399
|
+
takeaways: [
|
|
400
|
+
'Codex CLI buyers are usually looking for safe autonomy, not just more prompts.',
|
|
401
|
+
'ThumbGate sits in the critical gap between feedback and execution.',
|
|
402
|
+
'This page should rank for people who want guardrails without giving up CLI speed.',
|
|
403
|
+
],
|
|
404
|
+
sections: [
|
|
405
|
+
{
|
|
406
|
+
heading: 'What Codex CLI users usually need',
|
|
407
|
+
paragraphs: [
|
|
408
|
+
'The problem is rarely a single bad command. It is the cost of the same failure pattern showing up across branches, sessions, or rushed workflows. Once that pattern is obvious, the buyer wants a durable control point.',
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
heading: 'What ThumbGate adds',
|
|
413
|
+
bullets: [
|
|
414
|
+
'Feedback capture with explicit thumbs-up/down signals.',
|
|
415
|
+
'Searchable lessons and linked prevention rules.',
|
|
416
|
+
'Pre-action gates that block repeated bad commands before they run.',
|
|
417
|
+
'Verification evidence that gives teams something concrete to audit.',
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
heading: 'Why this matters for revenue',
|
|
422
|
+
paragraphs: [
|
|
423
|
+
'Guardrails are easier to buy when the outcome is obvious: less rework, fewer repeated failures, and a visible chain from operator feedback to enforced behavior.',
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
faq: [
|
|
428
|
+
{
|
|
429
|
+
question: 'Is ThumbGate only for Codex CLI?',
|
|
430
|
+
answer: 'No. Codex CLI is one supported workflow, but the same feedback and enforcement loop also works across Claude Code, Cursor, Gemini, Amp, and OpenCode.',
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
question: 'How are Codex CLI guardrails different from prompt instructions?',
|
|
434
|
+
answer: 'Prompt instructions are advisory. ThumbGate pre-action gates intercept the tool call itself and block the known-bad pattern before execution.',
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
relatedPaths: ['/guides/pre-action-gates', '/compare/mem0'],
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
query: 'gemini cli feedback memory',
|
|
441
|
+
path: '/guides/gemini-cli-feedback-memory',
|
|
442
|
+
pageType: 'integration',
|
|
443
|
+
pillar: 'agent-workflows',
|
|
444
|
+
title: 'Gemini CLI Feedback Memory | Memory Plus Enforcement with ThumbGate',
|
|
445
|
+
heroTitle: 'Gemini CLI Feedback Memory That Leads to Enforcement',
|
|
446
|
+
heroSummary: 'Gemini CLI users often start by asking for better memory. ThumbGate answers the bigger need: memory that can become prevention rules and pre-action gates when the same mistake shows up twice.',
|
|
447
|
+
takeaways: [
|
|
448
|
+
'Gemini CLI searchers often begin with memory but buy because of enforcement.',
|
|
449
|
+
'ThumbGate keeps the local-first memory story while adding runtime blocking.',
|
|
450
|
+
'The ideal conversion path here is memory query to product proof to Pro page.',
|
|
451
|
+
],
|
|
452
|
+
sections: [
|
|
453
|
+
{
|
|
454
|
+
heading: 'Why memory is only step one',
|
|
455
|
+
paragraphs: [
|
|
456
|
+
'Persistent memory helps Gemini CLI recall past context, but it still leaves a blind spot. Remembering that a workflow went badly is different from preventing the next risky action when the same pattern appears again.',
|
|
457
|
+
],
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
heading: 'What ThumbGate adds on top',
|
|
461
|
+
bullets: [
|
|
462
|
+
'Local-first lessons you can search across sessions.',
|
|
463
|
+
'Structured thumbs-up/down feedback for reinforcement and correction.',
|
|
464
|
+
'Prevention rules linked to past failures.',
|
|
465
|
+
'Pre-action gates that stop repeated mistakes before execution.',
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
heading: 'Who this is really for',
|
|
470
|
+
paragraphs: [
|
|
471
|
+
'This page is for operators who already know memory matters, but now need a reliability layer that protects live workflows instead of just preserving notes about them.',
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
faq: [
|
|
476
|
+
{
|
|
477
|
+
question: 'Does ThumbGate replace Gemini CLI memory?',
|
|
478
|
+
answer: 'No. ThumbGate extends the memory story with searchable lessons, rules, and gates so memory becomes operationally useful instead of purely historical.',
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
question: 'Can this stay local-first?',
|
|
482
|
+
answer: 'Yes. ThumbGate is built for local-first workflows, which lowers risk for developers who do not want sensitive history pushed into a hosted memory layer.',
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
relatedPaths: ['/compare/mem0', '/guides/stop-repeated-ai-agent-mistakes'],
|
|
486
|
+
},
|
|
276
487
|
{
|
|
277
488
|
query: 'claude desktop extension plugin thumbgate',
|
|
278
489
|
path: '/guides/claude-desktop',
|
|
@@ -444,7 +655,7 @@ function classifyIntent(query) {
|
|
|
444
655
|
return 'commercial';
|
|
445
656
|
}
|
|
446
657
|
if (/\b(what is|how to|guide|best practices|why)\b/.test(normalized)) return 'informational';
|
|
447
|
-
if (/\b(guardrails|pre-action gates|feedback|prevent repeated mistakes|memory)\b/.test(normalized)) {
|
|
658
|
+
if (/\b(guardrails|pre-action gates|feedback|prevent repeated mistakes|repeating mistakes|memory)\b/.test(normalized)) {
|
|
448
659
|
return 'commercial';
|
|
449
660
|
}
|
|
450
661
|
return 'informational';
|
|
@@ -454,7 +665,7 @@ function inferPillar(query) {
|
|
|
454
665
|
const normalized = normalizeText(query).toLowerCase();
|
|
455
666
|
if (/\b(speclock|mem0|alternative|vs|compare|comparison)\b/.test(normalized)) return 'comparison';
|
|
456
667
|
if (/\b(thumbs up|thumbs down|feedback|reinforce|mistake)\b/.test(normalized)) return 'feedback-loop';
|
|
457
|
-
if (/\b(pre-action gates|guardrails|block|prevent repeated mistakes)\b/.test(normalized)) return 'pre-action-gates';
|
|
668
|
+
if (/\b(pre-action gates|guardrails|block|prevent repeated mistakes|repeating mistakes)\b/.test(normalized)) return 'pre-action-gates';
|
|
458
669
|
if (/\b(claude code|cursor|codex|gemini|amp|opencode|integration|plugin)\b/.test(normalized)) return 'agent-workflows';
|
|
459
670
|
return 'ai-agent-reliability';
|
|
460
671
|
}
|
|
@@ -463,6 +674,8 @@ function inferPersona(query) {
|
|
|
463
674
|
const normalized = normalizeText(query).toLowerCase();
|
|
464
675
|
if (normalized.includes('claude code')) return 'claude-code-builder';
|
|
465
676
|
if (normalized.includes('cursor')) return 'cursor-builder';
|
|
677
|
+
if (normalized.includes('codex')) return 'codex-builder';
|
|
678
|
+
if (normalized.includes('gemini')) return 'gemini-builder';
|
|
466
679
|
if (/\b(vs|alternative|compare)\b/.test(normalized)) return 'tool-evaluator';
|
|
467
680
|
if (/\b(guardrails|pre-action gates)\b/.test(normalized)) return 'engineering-lead';
|
|
468
681
|
return 'ai-engineer';
|
|
@@ -628,8 +841,8 @@ function createPageSpec(blueprint, row) {
|
|
|
628
841
|
faq: blueprint.faq,
|
|
629
842
|
relatedPages,
|
|
630
843
|
cta: {
|
|
631
|
-
label: '
|
|
632
|
-
href:
|
|
844
|
+
label: 'See ThumbGate Pro',
|
|
845
|
+
href: `/pro?utm_source=website&utm_medium=seo_page&utm_campaign=${blueprint.path.split('/').filter(Boolean).join('_')}`,
|
|
633
846
|
},
|
|
634
847
|
proofLinks: [
|
|
635
848
|
{ label: 'Verification evidence', href: PRODUCT.verificationUrl },
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
listFeedbackArtifactPaths,
|
|
7
|
+
resolveFeedbackDir,
|
|
8
|
+
resolveProjectDir,
|
|
9
|
+
} = require('./feedback-paths');
|
|
6
10
|
|
|
7
11
|
function unique(values = []) {
|
|
8
12
|
return [...new Set(values.filter(Boolean).map((value) => path.resolve(value)))];
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
function getStatuslineCacheCandidates(options = {}) {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const feedbackDir = resolveFeedbackDir({
|
|
16
|
+
const env = options.env || process.env;
|
|
17
|
+
const projectDir = resolveProjectDir({ cwd: options.cwd, env });
|
|
18
|
+
const feedbackDir = resolveFeedbackDir({ projectDir, env });
|
|
15
19
|
|
|
16
20
|
return unique([
|
|
21
|
+
...listFeedbackArtifactPaths('statusline_cache.json', { projectDir, env }),
|
|
17
22
|
path.join(feedbackDir, 'statusline_cache.json'),
|
|
18
|
-
path.join(cwd, '.thumbgate', 'statusline_cache.json'),
|
|
19
|
-
home ? path.join(home, '.thumbgate', 'statusline_cache.json') : null,
|
|
20
23
|
]);
|
|
21
24
|
}
|
|
22
25
|
|