thumbgate 0.9.9 → 0.9.11
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/README.md +4 -4
- package/.claude-plugin/marketplace.json +4 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +115 -312
- package/adapters/README.md +2 -2
- package/adapters/amp/skills/{rlhf-feedback → thumbgate-feedback}/SKILL.md +1 -1
- package/adapters/chatgpt/openapi.yaml +2 -2
- package/adapters/claude/.mcp.json +3 -3
- package/adapters/codex/config.toml +4 -4
- package/adapters/gemini/function-declarations.json +1 -1
- package/adapters/mcp/server-stdio.js +66 -6
- package/adapters/opencode/opencode.json +4 -2
- package/bin/cli.js +188 -39
- package/config/e2e-critical-flows.json +4 -0
- package/config/gates/default.json +74 -2
- package/config/github-about.json +1 -1
- package/config/mcp-allowlists.json +33 -6
- package/config/skill-packs/react-testing.json +1 -1
- package/config/tessl-tiles.json +3 -3
- package/openapi/openapi.yaml +2 -2
- package/package.json +23 -9
- package/plugins/amp-skill/INSTALL.md +3 -2
- package/plugins/amp-skill/SKILL.md +1 -0
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-codex-bridge/.mcp.json +5 -3
- package/plugins/claude-codex-bridge/README.md +1 -1
- package/plugins/claude-codex-bridge/skills/setup/SKILL.md +1 -1
- package/plugins/claude-skill/INSTALL.md +4 -3
- package/plugins/claude-skill/SKILL.md +1 -1
- package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-profile/.mcp.json +5 -3
- package/plugins/codex-profile/INSTALL.md +2 -2
- package/plugins/codex-profile/README.md +1 -1
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-marketplace/README.md +5 -5
- package/plugins/cursor-marketplace/mcp.json +4 -2
- package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +1 -1
- package/plugins/cursor-marketplace/scripts/gate-check.sh +15 -5
- package/plugins/gemini-extension/INSTALL.md +4 -4
- package/plugins/opencode-profile/INSTALL.md +5 -5
- package/public/dashboard.html +15 -8
- package/public/index.html +134 -375
- package/public/js/buyer-intent.js +252 -0
- package/public/pro.html +1085 -0
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/adk-consolidator.js +17 -5
- package/scripts/agent-readiness.js +3 -1
- package/scripts/agent-security-hardening.js +4 -4
- package/scripts/auto-promote-gates.js +8 -0
- package/scripts/auto-wire-hooks.js +105 -21
- package/scripts/billing.js +111 -7
- package/scripts/build-metadata.js +14 -0
- package/scripts/check-congruence.js +1 -1
- package/scripts/context-engine.js +2 -1
- package/scripts/daemon-manager.js +2 -2
- package/scripts/dashboard.js +2 -2
- package/scripts/data-governance.js +1 -1
- package/scripts/deploy-gcp.sh +1 -1
- package/scripts/deploy-policy.js +22 -4
- package/scripts/dispatch-brief.js +1 -1
- package/scripts/ensure-repo-bootstrap.js +1 -1
- package/scripts/feedback-attribution.js +22 -10
- package/scripts/feedback-fallback.js +3 -2
- package/scripts/feedback-inbox-read.js +1 -1
- package/scripts/feedback-loop.js +41 -3
- package/scripts/feedback-paths.js +8 -8
- package/scripts/feedback-schema.js +1 -1
- package/scripts/feedback-to-memory.js +2 -2
- package/scripts/filesystem-search.js +2 -2
- package/scripts/gates-engine.js +765 -34
- package/scripts/generate-paperbanana-diagrams.sh +3 -3
- package/scripts/github-about.js +1 -1
- package/scripts/gtm-revenue-loop.js +20 -1
- package/scripts/hook-runtime.js +89 -0
- package/scripts/hook-stop-self-score.sh +3 -3
- package/scripts/hook-thumbgate-cache-updater.js +98 -37
- package/scripts/hosted-config.js +12 -10
- package/scripts/hybrid-feedback-context.js +54 -13
- package/scripts/install-mcp.js +14 -1
- package/scripts/intent-router.js +1 -1
- package/scripts/internal-agent-bootstrap.js +1 -1
- package/scripts/lesson-inference.js +6 -1
- package/scripts/license.js +54 -16
- package/scripts/mcp-config.js +69 -7
- package/scripts/memory-migration.js +1 -1
- package/scripts/money-watcher.js +166 -16
- package/scripts/operational-integrity.js +480 -0
- package/scripts/optimize-context.js +1 -1
- package/scripts/perplexity-marketing.js +1 -1
- package/scripts/post-everywhere.js +7 -12
- package/scripts/post-to-x.js +1 -1
- package/scripts/pr-manager.js +14 -11
- package/scripts/problem-detail.js +10 -10
- package/scripts/profile-router.js +2 -0
- package/scripts/prompt-dlp.js +1 -0
- package/scripts/prove-adapters.js +6 -6
- package/scripts/prove-automation.js +1 -1
- package/scripts/prove-autoresearch.js +1 -1
- package/scripts/prove-claim-verification.js +3 -3
- package/scripts/prove-data-pipeline.js +5 -5
- package/scripts/prove-data-quality.js +1 -1
- package/scripts/prove-evolution.js +7 -7
- package/scripts/prove-harnesses.js +2 -2
- package/scripts/prove-lancedb.js +2 -2
- package/scripts/prove-local-intelligence.js +1 -1
- package/scripts/prove-loop-closure.js +1 -1
- package/scripts/prove-predictive-insights.js +2 -2
- package/scripts/prove-runtime.js +6 -6
- package/scripts/prove-seo-gsd.js +1 -1
- package/scripts/prove-settings.js +4 -4
- package/scripts/prove-subway-upgrades.js +1 -1
- package/scripts/prove-tessl.js +2 -2
- package/scripts/prove-xmemory.js +2 -2
- package/scripts/publish-decision.js +10 -0
- package/scripts/published-cli.js +34 -0
- package/scripts/rate-limiter.js +2 -2
- package/scripts/reddit-monitor-cron.sh +2 -2
- package/scripts/reminder-engine.js +1 -1
- package/scripts/schedule-manager.js +3 -3
- package/scripts/self-healing-check.js +1 -1
- package/scripts/shieldcortex-memory-firewall-runner.mjs +1 -1
- package/scripts/skill-quality-tracker.js +1 -1
- package/scripts/social-analytics/db/social-analytics.db-shm +0 -0
- package/scripts/social-analytics/db/social-analytics.db-wal +0 -0
- package/scripts/social-analytics/engagement-audit.js +202 -0
- package/scripts/social-analytics/generate-instagram-card.js +1 -1
- package/scripts/social-analytics/instagram-thumbgate-post.js +5 -1
- package/scripts/social-analytics/install-growth-automation.js +114 -0
- package/scripts/social-analytics/publish-instagram-thumbgate.js +8 -2
- package/scripts/social-analytics/publish-thumbgate-launch.js +1 -1
- package/scripts/social-analytics/publishers/reddit.js +7 -12
- package/scripts/social-analytics/publishers/zernio.js +19 -0
- package/scripts/social-analytics/reconcile-thumbgate-campaign.js +165 -0
- package/scripts/social-analytics/schedule-thumbgate-campaign.js +275 -0
- package/scripts/social-analytics/sync-launch-assets.js +185 -0
- package/scripts/social-pipeline.js +2 -2
- package/scripts/social-post-hourly.js +185 -0
- package/scripts/social-quality-gate.js +119 -3
- package/scripts/social-reply-monitor.js +150 -34
- package/scripts/statusline-cache-path.js +27 -0
- package/scripts/statusline-meta.js +22 -0
- package/scripts/statusline.sh +24 -32
- package/scripts/sync-version.js +24 -12
- package/scripts/telemetry-analytics.js +4 -4
- package/scripts/tessl-export.js +1 -1
- package/scripts/test-coverage.js +20 -13
- package/scripts/thumbgate-search.js +2 -2
- package/scripts/tool-registry.js +98 -1
- package/scripts/train_from_feedback.py +1 -1
- package/scripts/user-profile.js +4 -4
- package/scripts/validate-feedback.js +1 -1
- package/scripts/vector-store.js +1 -1
- package/scripts/verification-loop.js +1 -1
- package/scripts/verify-run.js +1 -1
- package/scripts/weekly-auto-post.js +1 -1
- package/skills/{rlhf-feedback → thumbgate-feedback}/SKILL.md +1 -1
- package/src/api/server.js +291 -41
- package/scripts/__pycache__/train_from_feedback.cpython-314.pyc +0 -0
- package/scripts/social-analytics/db/social-analytics.db +0 -0
package/scripts/deploy-gcp.sh
CHANGED
package/scripts/deploy-policy.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const { normalizeOrigin } = require('./hosted-config');
|
|
4
|
+
const { DEFAULT_PUBLIC_APP_ORIGIN, normalizeOrigin } = require('./hosted-config');
|
|
5
5
|
|
|
6
6
|
const SECRET_POLICY = {
|
|
7
7
|
THUMBGATE_API_KEY: { rotatedAtEnv: 'THUMBGATE_API_KEY_ROTATED_AT', maxAgeDays: 30 },
|
|
@@ -39,6 +39,23 @@ const PROFILE_DEFS = {
|
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
function resolveEnvValue(name, env = process.env) {
|
|
43
|
+
const value = String(env[name] || '').trim();
|
|
44
|
+
if (value) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (name === 'THUMBGATE_PUBLIC_APP_ORIGIN') {
|
|
49
|
+
return DEFAULT_PUBLIC_APP_ORIGIN;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (name === 'THUMBGATE_BILLING_API_BASE_URL') {
|
|
53
|
+
return resolveEnvValue('THUMBGATE_PUBLIC_APP_ORIGIN', env);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
function parseTimestamp(value) {
|
|
43
60
|
if (!value || typeof value !== 'string') {
|
|
44
61
|
return null;
|
|
@@ -98,7 +115,7 @@ function evaluateDeployPolicy(env = process.env, { profiles = ['runtime'], now =
|
|
|
98
115
|
const errors = [];
|
|
99
116
|
|
|
100
117
|
for (const name of requiredVars) {
|
|
101
|
-
const value =
|
|
118
|
+
const value = resolveEnvValue(name, env);
|
|
102
119
|
if (!value) {
|
|
103
120
|
errors.push({ type: 'missing_variable', name, message: `${name} is required` });
|
|
104
121
|
continue;
|
|
@@ -114,7 +131,7 @@ function evaluateDeployPolicy(env = process.env, { profiles = ['runtime'], now =
|
|
|
114
131
|
}
|
|
115
132
|
|
|
116
133
|
for (const name of requiredSecrets) {
|
|
117
|
-
const secretValue =
|
|
134
|
+
const secretValue = resolveEnvValue(name, env);
|
|
118
135
|
if (!secretValue.trim()) {
|
|
119
136
|
errors.push({ type: 'missing_secret', name, message: `${name} is required` });
|
|
120
137
|
continue;
|
|
@@ -125,7 +142,7 @@ function evaluateDeployPolicy(env = process.env, { profiles = ['runtime'], now =
|
|
|
125
142
|
continue;
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
const rotatedAtRaw =
|
|
145
|
+
const rotatedAtRaw = resolveEnvValue(policy.rotatedAtEnv, env);
|
|
129
146
|
if (!rotatedAtRaw) {
|
|
130
147
|
errors.push({
|
|
131
148
|
type: 'missing_rotation_timestamp',
|
|
@@ -222,6 +239,7 @@ module.exports = {
|
|
|
222
239
|
PROFILE_DEFS,
|
|
223
240
|
parseTimestamp,
|
|
224
241
|
getAgeDays,
|
|
242
|
+
resolveEnvValue,
|
|
225
243
|
evaluateDeployPolicy,
|
|
226
244
|
formatReport,
|
|
227
245
|
};
|
|
@@ -9,7 +9,7 @@ const DISPATCH_TASK_DESCRIPTIONS = {
|
|
|
9
9
|
feedback_summary: 'Summarize recent wins, failures, and operator notes.',
|
|
10
10
|
search_lessons: 'Search promoted lessons and inspect what corrective action the system linked to each one.',
|
|
11
11
|
retrieve_lessons: 'Retrieve top-K relevant lessons for a given tool/action context (per-action guidance).',
|
|
12
|
-
|
|
12
|
+
search_thumbgate: 'Search raw ThumbGate feedback, ContextFS memory, and prevention rules.',
|
|
13
13
|
feedback_stats: 'Inspect approval trends and failure domains.',
|
|
14
14
|
diagnose_failure: 'Explain why a run failed or was blocked.',
|
|
15
15
|
list_intents: 'List available workflow plans without executing them.',
|
|
@@ -7,7 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const REPO_ROOT = path.resolve(process.argv[2] || process.cwd());
|
|
8
8
|
const RLHF_ENTRY = {
|
|
9
9
|
command: 'npx',
|
|
10
|
-
args: ['-y', '
|
|
10
|
+
args: ['-y', 'thumbgate@latest', 'serve'],
|
|
11
11
|
};
|
|
12
12
|
const LEGACY_SERVER_NAMES = ['thumbgate', 'rlhf_feedback_loop'];
|
|
13
13
|
const INFO_EXCLUDE_ENTRIES = ['.rlhf/', '.thumbgate/', '.mcp.json'];
|
|
@@ -5,12 +5,22 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
function getAttributionPaths(options = {}) {
|
|
9
|
+
const feedbackDir = resolveFeedbackDir({
|
|
10
|
+
cwd: options.cwd,
|
|
11
|
+
env: options.env,
|
|
12
|
+
feedbackDir: options.feedbackDir,
|
|
13
|
+
home: options.home,
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
feedbackDir,
|
|
17
|
+
actionLog: path.join(feedbackDir, 'action-log.jsonl'),
|
|
18
|
+
attributions: path.join(feedbackDir, 'feedback-attributions.jsonl'),
|
|
19
|
+
attributedFeedback: path.join(feedbackDir, 'attributed-feedback.jsonl'),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const PATHS = getAttributionPaths();
|
|
14
24
|
|
|
15
25
|
const STOPWORDS = new Set([
|
|
16
26
|
'about', 'after', 'again', 'allow', 'already', 'always', 'because', 'before', 'being', 'between',
|
|
@@ -155,7 +165,7 @@ function scoreCandidate(action, feedbackNormalized, feedbackTokens, nowMs) {
|
|
|
155
165
|
}
|
|
156
166
|
|
|
157
167
|
function recordAction(toolName, toolInput, opts = {}) {
|
|
158
|
-
const actionLogPath = opts.actionLogPath || process.env.THUMBGATE_ACTION_LOG ||
|
|
168
|
+
const actionLogPath = opts.actionLogPath || process.env.THUMBGATE_ACTION_LOG || getAttributionPaths(opts).actionLog;
|
|
159
169
|
const tool = String(toolName || 'unknown');
|
|
160
170
|
const inputSummary = summarizeToolInput(tool, toolInput);
|
|
161
171
|
const normalized = normalize(inputSummary);
|
|
@@ -175,9 +185,10 @@ function recordAction(toolName, toolInput, opts = {}) {
|
|
|
175
185
|
|
|
176
186
|
function attributeFeedback(signal, feedbackContext, opts = {}) {
|
|
177
187
|
const sig = String(signal || '').toLowerCase().trim();
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const
|
|
188
|
+
const paths = getAttributionPaths(opts);
|
|
189
|
+
const actionLogPath = opts.actionLogPath || process.env.THUMBGATE_ACTION_LOG || paths.actionLog;
|
|
190
|
+
const attributionsPath = opts.attributionsPath || process.env.THUMBGATE_FEEDBACK_ATTRIBUTIONS || paths.attributions;
|
|
191
|
+
const attributedFeedbackPath = opts.attributedFeedbackPath || process.env.THUMBGATE_ATTRIBUTED_FEEDBACK || paths.attributedFeedback;
|
|
181
192
|
|
|
182
193
|
if (sig !== 'negative' && sig !== 'positive') {
|
|
183
194
|
return { ok: true, skipped: true, reason: 'signal_not_supported' };
|
|
@@ -298,6 +309,7 @@ if (require.main === module) {
|
|
|
298
309
|
|
|
299
310
|
module.exports = {
|
|
300
311
|
PATHS,
|
|
312
|
+
getAttributionPaths,
|
|
301
313
|
readJsonl,
|
|
302
314
|
appendJsonl,
|
|
303
315
|
normalize,
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
|
|
14
14
|
const http = require('http');
|
|
15
15
|
const https = require('https');
|
|
16
|
+
const { DEFAULT_PUBLIC_APP_ORIGIN } = require('./hosted-config');
|
|
16
17
|
|
|
17
18
|
const ENDPOINTS = [
|
|
18
|
-
{ url: 'http://localhost:
|
|
19
|
-
{ url:
|
|
19
|
+
{ url: 'http://localhost:3456/v1/feedback/capture', key: process.env.THUMBGATE_API_KEY || 'tg_creator_dev_enterprise', label: 'local' },
|
|
20
|
+
{ url: `${DEFAULT_PUBLIC_APP_ORIGIN}/v1/feedback/capture`, key: process.env.THUMBGATE_API_KEY || 'tg_creator_dev_enterprise', label: 'hosted' },
|
|
20
21
|
];
|
|
21
22
|
|
|
22
23
|
function parseArgs() {
|
|
@@ -94,7 +94,7 @@ function runTests() {
|
|
|
94
94
|
const entries = [
|
|
95
95
|
{ signal: 'negative', context: 'Bad thing happened', tags: ['testing'] },
|
|
96
96
|
{ signal: 'positive', context: 'Good thing happened', tags: ['testing'] },
|
|
97
|
-
{ signal: 'negative', context: 'Another bad thing', tags: ['
|
|
97
|
+
{ signal: 'negative', context: 'Another bad thing', tags: ['thumbgate'] },
|
|
98
98
|
];
|
|
99
99
|
fs.writeFileSync(tmpInbox, entries.map((e) => JSON.stringify(e)).join('\n') + '\n');
|
|
100
100
|
|
package/scripts/feedback-loop.js
CHANGED
|
@@ -105,8 +105,7 @@ const pendingBackgroundSideEffects = new Set();
|
|
|
105
105
|
*/
|
|
106
106
|
function updateStatuslineWithLesson({ accepted, signal, memoryId, feedbackId, lesson, turnCount }) {
|
|
107
107
|
try {
|
|
108
|
-
const
|
|
109
|
-
const cachePath = path.join(cacheDir, '.thumbgate', 'statusline_cache.json');
|
|
108
|
+
const cachePath = path.join(getFeedbackPaths().FEEDBACK_DIR, 'statusline_cache.json');
|
|
110
109
|
let cache = {};
|
|
111
110
|
try {
|
|
112
111
|
cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
@@ -138,6 +137,12 @@ function updateStatuslineWithLesson({ accepted, signal, memoryId, feedbackId, le
|
|
|
138
137
|
cache.updated_at = String(Math.floor(Date.now() / 1000));
|
|
139
138
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
140
139
|
fs.writeFileSync(cachePath, JSON.stringify(cache));
|
|
140
|
+
try {
|
|
141
|
+
const { refreshStatuslineCache } = require('./hook-thumbgate-cache-updater');
|
|
142
|
+
refreshStatuslineCache(analyzeFeedback(), cachePath);
|
|
143
|
+
} catch {
|
|
144
|
+
/* keep lesson refresh best-effort */
|
|
145
|
+
}
|
|
141
146
|
} catch { /* statusline update is best-effort */ }
|
|
142
147
|
}
|
|
143
148
|
|
|
@@ -615,6 +620,35 @@ function enrichFeedbackContext(feedbackEvent, params) {
|
|
|
615
620
|
? params.filePaths.split(',').map((f) => f.trim()).filter(Boolean)
|
|
616
621
|
: [];
|
|
617
622
|
const errorType = params.errorType || null;
|
|
623
|
+
const protectedFiles = filePaths.filter((filePath) => /(^|\/)(agents\.md|claude(\.local)?\.md|gemini\.md|readme\.md|\.gitignore|skill\.md)$|^\.husky\/|^config\/gates\//i.test(filePath));
|
|
624
|
+
const combinedText = [
|
|
625
|
+
feedbackEvent.context || '',
|
|
626
|
+
feedbackEvent.whatWentWrong || '',
|
|
627
|
+
feedbackEvent.whatToChange || '',
|
|
628
|
+
...(Array.isArray(feedbackEvent.tags) ? feedbackEvent.tags : []),
|
|
629
|
+
].join(' ').toLowerCase();
|
|
630
|
+
const includesPhrase = (phrase) => combinedText.includes(phrase);
|
|
631
|
+
const includesOrderedTerms = (firstTerm, secondTerm) => {
|
|
632
|
+
const firstIndex = combinedText.indexOf(firstTerm);
|
|
633
|
+
if (firstIndex === -1) return false;
|
|
634
|
+
return combinedText.indexOf(secondTerm, firstIndex + firstTerm.length) !== -1;
|
|
635
|
+
};
|
|
636
|
+
const enforcement = {
|
|
637
|
+
scopeViolation: includesPhrase('scope creep')
|
|
638
|
+
|| includesPhrase('out of scope')
|
|
639
|
+
|| includesOrderedTerms('outside', 'scope')
|
|
640
|
+
|| includesPhrase('wrong files')
|
|
641
|
+
|| includesPhrase('unrelated files'),
|
|
642
|
+
approvalFailure: includesPhrase('without approval')
|
|
643
|
+
|| includesPhrase('missing approval')
|
|
644
|
+
|| includesPhrase('approval required')
|
|
645
|
+
|| includesPhrase('permission required'),
|
|
646
|
+
protectedFileViolation: protectedFiles.length > 0
|
|
647
|
+
|| includesPhrase('protected file')
|
|
648
|
+
|| includesPhrase('policy file')
|
|
649
|
+
|| includesPhrase('hook file'),
|
|
650
|
+
protectedFiles,
|
|
651
|
+
};
|
|
618
652
|
|
|
619
653
|
return {
|
|
620
654
|
...feedbackEvent,
|
|
@@ -623,6 +657,7 @@ function enrichFeedbackContext(feedbackEvent, params) {
|
|
|
623
657
|
filePaths,
|
|
624
658
|
errorType,
|
|
625
659
|
outcomeCategory,
|
|
660
|
+
enforcement,
|
|
626
661
|
},
|
|
627
662
|
};
|
|
628
663
|
} catch (_err) {
|
|
@@ -812,6 +847,9 @@ function inferLessonFromConversation(conversationWindow, signal) {
|
|
|
812
847
|
const tags = [];
|
|
813
848
|
if (filePaths.length > 0) tags.push('has-file-context');
|
|
814
849
|
if (errorPatterns.length > 0) tags.push('has-error-context');
|
|
850
|
+
if (filePaths.some((filePath) => /(^|\/)(agents\.md|claude(\.local)?\.md|gemini\.md|readme\.md|\.gitignore|skill\.md)$|^\.husky\/|^config\/gates\//i.test(filePath))) {
|
|
851
|
+
tags.push('protected-file-context');
|
|
852
|
+
}
|
|
815
853
|
|
|
816
854
|
return {
|
|
817
855
|
lesson,
|
|
@@ -1753,7 +1791,7 @@ function runTests() {
|
|
|
1753
1791
|
}
|
|
1754
1792
|
}
|
|
1755
1793
|
|
|
1756
|
-
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), '
|
|
1794
|
+
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'thumbgate-loop-test-'));
|
|
1757
1795
|
const localFeedbackLog = path.join(tmpDir, 'feedback-log.jsonl');
|
|
1758
1796
|
process.env.THUMBGATE_FEEDBACK_DIR = tmpDir;
|
|
1759
1797
|
|
|
@@ -62,12 +62,12 @@ function getThumbgateFeedbackDir(options = {}) {
|
|
|
62
62
|
return path.join(cwd, '.thumbgate');
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
function
|
|
65
|
+
function getFallbackFeedbackDir(options = {}) {
|
|
66
66
|
const env = options.env || process.env;
|
|
67
|
-
if (env.
|
|
68
|
-
if (env.
|
|
67
|
+
if (env._TEST_THUMBGATE_FALLBACK_FEEDBACK_DIR) return env._TEST_THUMBGATE_FALLBACK_FEEDBACK_DIR;
|
|
68
|
+
if (env.THUMBGATE_FALLBACK_FEEDBACK_DIR) return env.THUMBGATE_FALLBACK_FEEDBACK_DIR;
|
|
69
69
|
const cwd = options.cwd || process.cwd();
|
|
70
|
-
return path.join(cwd, '.
|
|
70
|
+
return path.join(cwd, '.thumbgate-compat');
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function getLegacyFeedbackDir(options = {}) {
|
|
@@ -91,8 +91,8 @@ function resolveFeedbackDir(options = {}) {
|
|
|
91
91
|
const localThumbgate = getThumbgateFeedbackDir(options);
|
|
92
92
|
if (dirExists(localThumbgate)) return localThumbgate;
|
|
93
93
|
|
|
94
|
-
const
|
|
95
|
-
if (dirExists(
|
|
94
|
+
const localFallback = getFallbackFeedbackDir(options);
|
|
95
|
+
if (dirExists(localFallback)) return localFallback;
|
|
96
96
|
|
|
97
97
|
const localLegacy = getLegacyFeedbackDir(options);
|
|
98
98
|
if (dirExists(localLegacy)) return localLegacy;
|
|
@@ -107,7 +107,7 @@ function getFeedbackPaths(options = {}) {
|
|
|
107
107
|
function listFallbackFeedbackDirs(options = {}) {
|
|
108
108
|
const activeDir = path.resolve(resolveFeedbackDir(options));
|
|
109
109
|
return uniquePaths([
|
|
110
|
-
|
|
110
|
+
getFallbackFeedbackDir(options),
|
|
111
111
|
getLegacyFeedbackDir(options),
|
|
112
112
|
]).filter((dirPath) => path.resolve(dirPath) !== activeDir);
|
|
113
113
|
}
|
|
@@ -136,7 +136,7 @@ module.exports = {
|
|
|
136
136
|
getFeedbackPaths,
|
|
137
137
|
getGlobalFeedbackDir,
|
|
138
138
|
getLegacyFeedbackDir,
|
|
139
|
-
|
|
139
|
+
getFallbackFeedbackDir,
|
|
140
140
|
getThumbgateFeedbackDir,
|
|
141
141
|
listFallbackFeedbackDirs,
|
|
142
142
|
listFeedbackArtifactPaths,
|
|
@@ -31,7 +31,7 @@ const {
|
|
|
31
31
|
} = require('./feedback-quality');
|
|
32
32
|
|
|
33
33
|
const INFERRED_TAG_RULES = [
|
|
34
|
-
{ tag: 'thumbgate', keywords: ['thumbgate', '
|
|
34
|
+
{ tag: 'thumbgate', keywords: ['thumbgate', 'feedback-loop', 'statusline', 'dashboard', 'mcp'] },
|
|
35
35
|
{ tag: 'testing', keywords: ['test', 'testing', 'jest', 'coverage', 'verify', 'verification'] },
|
|
36
36
|
{ tag: 'security', keywords: ['security', 'secret', 'credential', 'token', 'auth'] },
|
|
37
37
|
{ tag: 'performance', keywords: ['perf', 'performance', 'latency', 'slow'] },
|
|
@@ -119,7 +119,7 @@ function runTests() {
|
|
|
119
119
|
const pos = convertFeedbackToMemory({
|
|
120
120
|
signal: 'positive',
|
|
121
121
|
whatWorked: 'Built schema-validated feedback system with prevention rules',
|
|
122
|
-
tags: ['architecture', '
|
|
122
|
+
tags: ['architecture', 'thumbgate'],
|
|
123
123
|
});
|
|
124
124
|
assert(pos.ok === true, 'valid positive → ok');
|
|
125
125
|
assert(pos.actionType === 'store-learning', 'positive → store-learning');
|
|
@@ -151,7 +151,7 @@ function runTests() {
|
|
|
151
151
|
const ctxNeg = convertFeedbackToMemory({
|
|
152
152
|
signal: 'negative',
|
|
153
153
|
context: 'Showed fake ThumbGate statistics panel to user',
|
|
154
|
-
tags: ['
|
|
154
|
+
tags: ['thumbgate'],
|
|
155
155
|
});
|
|
156
156
|
assert(ctxNeg.ok === true, 'context-only negative → ok');
|
|
157
157
|
|
|
@@ -328,7 +328,7 @@ function getLastEmbeddingProfile() {
|
|
|
328
328
|
|
|
329
329
|
function getVersionSnapshot() {
|
|
330
330
|
return Promise.resolve({
|
|
331
|
-
|
|
331
|
+
thumbgate_memories: null,
|
|
332
332
|
prevention_rules: null,
|
|
333
333
|
context_packs: null,
|
|
334
334
|
engine: 'filesystem-search',
|
|
@@ -394,7 +394,7 @@ module.exports = {
|
|
|
394
394
|
getEmbeddingConfig,
|
|
395
395
|
getLastEmbeddingProfile,
|
|
396
396
|
getVersionSnapshot,
|
|
397
|
-
TABLE_NAME: '
|
|
397
|
+
TABLE_NAME: 'thumbgate_memories',
|
|
398
398
|
TABLE_PREVENTION_RULES: 'prevention_rules',
|
|
399
399
|
TABLE_CONTEXT_PACKS: 'context_packs',
|
|
400
400
|
|