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
|
@@ -48,7 +48,7 @@ budget_check() {
|
|
|
48
48
|
|
|
49
49
|
mkdir -p docs/diagrams
|
|
50
50
|
|
|
51
|
-
cat > docs/diagrams/
|
|
51
|
+
cat > docs/diagrams/thumbgate-architecture.txt <<'TXT'
|
|
52
52
|
The system starts when a user gives explicit thumbs up/down feedback. A capture layer enriches the signal with context and tags.
|
|
53
53
|
An action resolver maps the signal to store-learning, store-mistake, or no-action.
|
|
54
54
|
A schema validator enforces strict structure before memory promotion.
|
|
@@ -73,14 +73,14 @@ TXT
|
|
|
73
73
|
|
|
74
74
|
budget_check "$PB_ESTIMATE_PER_DIAGRAM"
|
|
75
75
|
paperbanana generate \
|
|
76
|
-
--input docs/diagrams/
|
|
76
|
+
--input docs/diagrams/thumbgate-architecture.txt \
|
|
77
77
|
--caption "ThumbGate architecture for AI coding agents with schema gate, memory store, prevention rules, and DPO export" \
|
|
78
78
|
--vlm-provider gemini \
|
|
79
79
|
--vlm-model "$PB_VLM_MODEL" \
|
|
80
80
|
--image-provider google_imagen \
|
|
81
81
|
--image-model "$PB_IMAGE_MODEL" \
|
|
82
82
|
--iterations "$PB_MAX_ITERATIONS" \
|
|
83
|
-
--output docs/diagrams/
|
|
83
|
+
--output docs/diagrams/thumbgate-architecture.png
|
|
84
84
|
node scripts/budget-guard.js --add="$PB_ESTIMATE_PER_DIAGRAM" --source=paperbanana --note="architecture-overview"
|
|
85
85
|
|
|
86
86
|
budget_check "$PB_ESTIMATE_PER_DIAGRAM"
|
package/scripts/github-about.js
CHANGED
|
@@ -79,7 +79,7 @@ function buildCanonicalRepoUrls(about) {
|
|
|
79
79
|
licenseUrl: `${about.repositoryUrl}/blob/main/LICENSE`,
|
|
80
80
|
quickStartUrl: `${about.repositoryUrl}#quick-start`,
|
|
81
81
|
pluginsUrl: `${about.repositoryUrl}/tree/main/plugins`,
|
|
82
|
-
readmeImageUrl: `https://raw.githubusercontent.com/${about.repo}/main/docs/diagrams/
|
|
82
|
+
readmeImageUrl: `https://raw.githubusercontent.com/${about.repo}/main/docs/diagrams/thumbgate-architecture-pb.png`,
|
|
83
83
|
verificationEvidenceUrl: `${about.repositoryUrl}/blob/main/docs/VERIFICATION_EVIDENCE.md`,
|
|
84
84
|
compatibilityReportUrl: `${about.repositoryUrl}/blob/main/proof/compatibility/report.json`,
|
|
85
85
|
automationReportUrl: `${about.repositoryUrl}/blob/main/proof/automation/report.json`,
|
|
@@ -4,13 +4,20 @@
|
|
|
4
4
|
const fs = require('node:fs');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
const { spawnSync } = require('node:child_process');
|
|
7
|
-
const { GoogleGenAI } = require('@google/genai');
|
|
8
7
|
const { resolveHostedBillingConfig } = require('./hosted-config');
|
|
9
8
|
const { getOperationalBillingSummary } = require('./operational-summary');
|
|
10
9
|
|
|
11
10
|
const COMMERCIAL_TRUTH_LINK = 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/COMMERCIAL_TRUTH.md';
|
|
12
11
|
const VERIFICATION_EVIDENCE_LINK = 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md';
|
|
13
12
|
|
|
13
|
+
function getGoogleGenAI() {
|
|
14
|
+
try {
|
|
15
|
+
return require('@google/genai').GoogleGenAI;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
function parseArgs(argv = []) {
|
|
15
22
|
const options = {
|
|
16
23
|
maxTargets: 6,
|
|
@@ -310,6 +317,18 @@ async function generateOutreachMessages(targets, motionCatalog = buildMotionCata
|
|
|
310
317
|
});
|
|
311
318
|
}
|
|
312
319
|
|
|
320
|
+
const GoogleGenAI = getGoogleGenAI();
|
|
321
|
+
if (!GoogleGenAI) {
|
|
322
|
+
return targets.map((target) => {
|
|
323
|
+
const selectedMotion = selectOutreachMotion(target, motionCatalog);
|
|
324
|
+
return {
|
|
325
|
+
...target,
|
|
326
|
+
selectedMotion,
|
|
327
|
+
message: buildFallbackMessage(target, selectedMotion, motionCatalog),
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
313
332
|
const ai = new GoogleGenAI({ apiKey });
|
|
314
333
|
const results = [];
|
|
315
334
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const {
|
|
6
|
+
isSourceCheckout,
|
|
7
|
+
publishedCliAvailable,
|
|
8
|
+
} = require('./mcp-config');
|
|
9
|
+
const { runPublishedCliHelp, publishedCliArgs } = require('./published-cli');
|
|
10
|
+
|
|
11
|
+
const PKG_ROOT = path.join(__dirname, '..');
|
|
12
|
+
const featureSupportCache = new Map();
|
|
13
|
+
|
|
14
|
+
function packageVersion() {
|
|
15
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'));
|
|
16
|
+
return pkg.version;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function shellQuote(value) {
|
|
20
|
+
return JSON.stringify(String(value));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function publishedHookCommandsAvailable(version) {
|
|
24
|
+
if (!publishedCliAvailable(version)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (featureSupportCache.has(version)) {
|
|
28
|
+
return featureSupportCache.get(version);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let available = false;
|
|
32
|
+
try {
|
|
33
|
+
const helpText = runPublishedCliHelp(version, { timeout: 8000 });
|
|
34
|
+
available = ['gate-check', 'cache-update', 'statusline-render', 'hook-auto-capture', 'session-start']
|
|
35
|
+
.every((command) => helpText.includes(command));
|
|
36
|
+
} catch {
|
|
37
|
+
available = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
featureSupportCache.set(version, available);
|
|
41
|
+
return available;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveCliBaseCommand() {
|
|
45
|
+
const version = packageVersion();
|
|
46
|
+
if (publishedHookCommandsAvailable(version)) {
|
|
47
|
+
return `npx ${publishedCliArgs(version).map(shellQuote).join(' ')}`;
|
|
48
|
+
}
|
|
49
|
+
if (isSourceCheckout(PKG_ROOT)) {
|
|
50
|
+
return `node ${shellQuote(path.join(PKG_ROOT, 'bin', 'cli.js'))}`;
|
|
51
|
+
}
|
|
52
|
+
return `npx -y thumbgate@${version}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildPortableHookCommand(subcommand) {
|
|
56
|
+
return `${resolveCliBaseCommand()} ${subcommand}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function preToolHookCommand() {
|
|
60
|
+
return buildPortableHookCommand('gate-check');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function userPromptHookCommand() {
|
|
64
|
+
return buildPortableHookCommand('hook-auto-capture');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sessionStartHookCommand() {
|
|
68
|
+
return buildPortableHookCommand('session-start');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function cacheUpdateHookCommand() {
|
|
72
|
+
return buildPortableHookCommand('cache-update');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function statuslineCommand() {
|
|
76
|
+
return buildPortableHookCommand('statusline-render');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
buildPortableHookCommand,
|
|
81
|
+
cacheUpdateHookCommand,
|
|
82
|
+
packageVersion,
|
|
83
|
+
publishedHookCommandsAvailable,
|
|
84
|
+
preToolHookCommand,
|
|
85
|
+
resolveCliBaseCommand,
|
|
86
|
+
sessionStartHookCommand,
|
|
87
|
+
statuslineCommand,
|
|
88
|
+
userPromptHookCommand,
|
|
89
|
+
};
|
|
@@ -18,9 +18,9 @@ node -e '
|
|
|
18
18
|
const path = require("path");
|
|
19
19
|
|
|
20
20
|
// Resolve modules relative to ThumbGate package root
|
|
21
|
-
const
|
|
22
|
-
const { selfAuditAndLog } = require(path.join(
|
|
23
|
-
const { getFeedbackPaths } = require(path.join(
|
|
21
|
+
const thumbgateRoot = process.env.THUMBGATE_ROOT;
|
|
22
|
+
const { selfAuditAndLog } = require(path.join(thumbgateRoot, "scripts", "rlaif-self-audit"));
|
|
23
|
+
const { getFeedbackPaths } = require(path.join(thumbgateRoot, "scripts", "feedback-loop"));
|
|
24
24
|
|
|
25
25
|
const stopReason = process.env.CLAUDE_STOP_REASON || "unknown";
|
|
26
26
|
|
|
@@ -1,48 +1,109 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
2
4
|
/**
|
|
3
|
-
* PostToolUse hook: updates ThumbGate statusline cache after
|
|
4
|
-
*
|
|
5
|
+
* PostToolUse hook: updates ThumbGate statusline cache after dashboard/stat calls.
|
|
6
|
+
* Also used directly by the CLI to refresh statusline counters after feedback capture.
|
|
5
7
|
*/
|
|
6
|
-
|
|
8
|
+
|
|
7
9
|
const fs = require('fs');
|
|
8
10
|
const path = require('path');
|
|
9
11
|
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
function getCachePath() {
|
|
14
|
+
return path.join(resolveFeedbackDir(), 'statusline_cache.json');
|
|
15
|
+
}
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
process.stdin.setEncoding('utf8');
|
|
15
|
-
process.stdin.on('data', chunk => { input += chunk; });
|
|
16
|
-
process.stdin.on('end', () => {
|
|
17
|
+
function readExistingCache(cachePath = getCachePath()) {
|
|
17
18
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
return JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
20
|
+
} catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function writeStatuslineCache(nextCache, cachePath = getCachePath()) {
|
|
26
|
+
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
27
|
+
fs.writeFileSync(cachePath, JSON.stringify(nextCache));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizeDashboardPayload(payload = {}) {
|
|
31
|
+
const approval = payload.approval || {};
|
|
32
|
+
return {
|
|
33
|
+
thumbs_up: String(approval.totalPositive || 0),
|
|
34
|
+
thumbs_down: String(approval.totalNegative || 0),
|
|
35
|
+
lessons: String((payload.rubric || {}).samples || 0),
|
|
36
|
+
approval_rate: String(approval.approvalRate || '?'),
|
|
37
|
+
trend: approval.trendDirection || '?',
|
|
38
|
+
total_feedback: String(approval.total || 0),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeStatsPayload(payload = {}) {
|
|
43
|
+
return {
|
|
44
|
+
thumbs_up: String(payload.totalPositive || 0),
|
|
45
|
+
thumbs_down: String(payload.totalNegative || 0),
|
|
46
|
+
lessons: String((payload.rubric || {}).samples || 0),
|
|
47
|
+
approval_rate: String(Math.round((payload.approvalRate || 0) * 1000) / 10),
|
|
48
|
+
trend: payload.trend || '?',
|
|
49
|
+
total_feedback: String(payload.total || 0),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function refreshStatuslineCache(statsPayload = {}, cachePath = getCachePath()) {
|
|
54
|
+
const cache = {
|
|
55
|
+
...readExistingCache(cachePath),
|
|
56
|
+
...normalizeStatsPayload(statsPayload),
|
|
57
|
+
updated_at: String(Math.floor(Date.now() / 1000)),
|
|
58
|
+
};
|
|
59
|
+
writeStatuslineCache(cache, cachePath);
|
|
60
|
+
return cache;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function updateCacheFromEvent(event = {}, cachePath = getCachePath()) {
|
|
64
|
+
const tool = event.tool_name || '';
|
|
65
|
+
if (tool !== 'mcp__thumbgate__feedback_stats' && tool !== 'mcp__thumbgate__dashboard') {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const raw = event.tool_response;
|
|
70
|
+
if (!raw) return null;
|
|
71
|
+
const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
72
|
+
const cache = {
|
|
73
|
+
...readExistingCache(cachePath),
|
|
74
|
+
...(tool === 'mcp__thumbgate__feedback_stats'
|
|
75
|
+
? normalizeStatsPayload(data)
|
|
76
|
+
: normalizeDashboardPayload(data)),
|
|
77
|
+
updated_at: String(Math.floor(Date.now() / 1000)),
|
|
78
|
+
};
|
|
79
|
+
writeStatuslineCache(cache, cachePath);
|
|
80
|
+
return cache;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function runFromStdin() {
|
|
84
|
+
let input = '';
|
|
85
|
+
process.stdin.setEncoding('utf8');
|
|
86
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
87
|
+
process.stdin.on('end', () => {
|
|
88
|
+
try {
|
|
89
|
+
updateCacheFromEvent(input ? JSON.parse(input) : {});
|
|
90
|
+
} catch {
|
|
91
|
+
/* statusline cache is best-effort */
|
|
42
92
|
}
|
|
43
|
-
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (require.main === module) {
|
|
97
|
+
runFromStdin();
|
|
98
|
+
}
|
|
44
99
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
100
|
+
module.exports = {
|
|
101
|
+
getCachePath,
|
|
102
|
+
normalizeDashboardPayload,
|
|
103
|
+
normalizeStatsPayload,
|
|
104
|
+
readExistingCache,
|
|
105
|
+
refreshStatuslineCache,
|
|
106
|
+
runFromStdin,
|
|
107
|
+
updateCacheFromEvent,
|
|
108
|
+
writeStatuslineCache,
|
|
109
|
+
};
|
package/scripts/hosted-config.js
CHANGED
|
@@ -76,6 +76,10 @@ function normalizeTrackingId(value, pattern) {
|
|
|
76
76
|
return trimmed;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function resolveNormalizedOrigin(value, fallback = '') {
|
|
80
|
+
return normalizeOrigin(value) || normalizeOrigin(fallback);
|
|
81
|
+
}
|
|
82
|
+
|
|
79
83
|
function joinPublicUrl(baseOrigin, pathname) {
|
|
80
84
|
const normalized = normalizeOrigin(baseOrigin);
|
|
81
85
|
if (!normalized) {
|
|
@@ -112,16 +116,14 @@ function buildHostedCancelUrl(appOrigin, traceId) {
|
|
|
112
116
|
return `${joinPublicUrl(appOrigin, '/cancel')}${traceQuery}`;
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
function resolveHostedBillingConfig({ requestOrigin } = {}) {
|
|
119
|
+
function resolveHostedBillingConfig({ requestOrigin } = {}, env = process.env) {
|
|
116
120
|
const inferredOrigin = normalizeOrigin(requestOrigin) || DEFAULT_PUBLIC_APP_ORIGIN;
|
|
117
|
-
const appOrigin =
|
|
118
|
-
const billingApiBaseUrl =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
const gaMeasurementId = normalizeTrackingId(process.env.THUMBGATE_GA_MEASUREMENT_ID, GA_MEASUREMENT_ID_PATTERN);
|
|
124
|
-
const googleSiteVerification = normalizeTrackingId(process.env.THUMBGATE_GOOGLE_SITE_VERIFICATION);
|
|
121
|
+
const appOrigin = resolveNormalizedOrigin(env.THUMBGATE_PUBLIC_APP_ORIGIN, inferredOrigin) || inferredOrigin;
|
|
122
|
+
const billingApiBaseUrl = resolveNormalizedOrigin(env.THUMBGATE_BILLING_API_BASE_URL, appOrigin) || appOrigin;
|
|
123
|
+
const proPriceDollars = normalizePriceDollars(env.THUMBGATE_PRO_PRICE_DOLLARS) || DEFAULT_PRO_PRICE_DOLLARS;
|
|
124
|
+
const proPriceLabel = env.THUMBGATE_PRO_PRICE_LABEL || DEFAULT_PRO_PRICE_LABEL;
|
|
125
|
+
const gaMeasurementId = normalizeTrackingId(env.THUMBGATE_GA_MEASUREMENT_ID, GA_MEASUREMENT_ID_PATTERN);
|
|
126
|
+
const googleSiteVerification = normalizeTrackingId(env.THUMBGATE_GOOGLE_SITE_VERIFICATION);
|
|
125
127
|
|
|
126
128
|
return {
|
|
127
129
|
appOrigin,
|
|
@@ -129,7 +131,7 @@ function resolveHostedBillingConfig({ requestOrigin } = {}) {
|
|
|
129
131
|
checkoutEndpoint: joinPublicUrl(billingApiBaseUrl, '/v1/billing/checkout'),
|
|
130
132
|
sessionEndpoint: joinPublicUrl(billingApiBaseUrl, '/v1/billing/session'),
|
|
131
133
|
checkoutFallbackUrl: normalizeAbsoluteUrl(
|
|
132
|
-
|
|
134
|
+
env.THUMBGATE_CHECKOUT_FALLBACK_URL || DEFAULT_CHECKOUT_FALLBACK_URL
|
|
133
135
|
) || DEFAULT_CHECKOUT_FALLBACK_URL,
|
|
134
136
|
proPriceDollars,
|
|
135
137
|
proPriceLabel,
|
|
@@ -22,14 +22,24 @@ const { resolveFeedbackDir } = require('./feedback-paths');
|
|
|
22
22
|
// Paths
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
function getHybridPaths(options = {}) {
|
|
26
|
+
const feedbackDir = resolveFeedbackDir({
|
|
27
|
+
cwd: options.cwd,
|
|
28
|
+
env: options.env,
|
|
29
|
+
feedbackDir: options.feedbackDir,
|
|
30
|
+
home: options.home,
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
feedbackDir,
|
|
34
|
+
feedbackLog: path.join(feedbackDir, 'feedback-log.jsonl'),
|
|
35
|
+
inbox: path.join(feedbackDir, 'inbox.jsonl'),
|
|
36
|
+
pendingSync: path.join(feedbackDir, 'pending_cortex_sync.jsonl'),
|
|
37
|
+
attributedFeedback: path.join(feedbackDir, 'attributed-feedback.jsonl'),
|
|
38
|
+
guardArtifact: path.join(feedbackDir, 'pretool-guards.json'),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const PATHS = getHybridPaths();
|
|
33
43
|
|
|
34
44
|
// ---------------------------------------------------------------------------
|
|
35
45
|
// Constants
|
|
@@ -192,10 +202,11 @@ function hashText(text) {
|
|
|
192
202
|
*/
|
|
193
203
|
function buildHybridState(opts) {
|
|
194
204
|
const o = opts || {};
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
const
|
|
205
|
+
const paths = getHybridPaths(o);
|
|
206
|
+
const feedbackLogPath = o.feedbackLogPath || process.env.THUMBGATE_FEEDBACK_LOG || paths.feedbackLog;
|
|
207
|
+
const inboxPath = o.inboxPath || process.env.THUMBGATE_FEEDBACK_INBOX || paths.inbox;
|
|
208
|
+
const pendingSyncPath = o.pendingSyncPath || process.env.THUMBGATE_PENDING_SYNC || paths.pendingSync;
|
|
209
|
+
const attributedFeedbackPath = o.attributedFeedbackPath || process.env.THUMBGATE_ATTRIBUTED_FEEDBACK || paths.attributedFeedback;
|
|
199
210
|
|
|
200
211
|
const feedbackEntries = readJsonl(feedbackLogPath);
|
|
201
212
|
const inboxEntries = readJsonl(inboxPath);
|
|
@@ -236,6 +247,21 @@ function buildHybridState(opts) {
|
|
|
236
247
|
entry.context || '',
|
|
237
248
|
entry.whatWentWrong || entry.what_went_wrong || '',
|
|
238
249
|
entry.whatToChange || entry.what_to_change || '',
|
|
250
|
+
entry.failureType || '',
|
|
251
|
+
...(Array.isArray(entry.tags) ? entry.tags : []),
|
|
252
|
+
...(entry.richContext && Array.isArray(entry.richContext.filePaths) ? entry.richContext.filePaths : []),
|
|
253
|
+
...(entry.structuredRule && entry.structuredRule.metadata && Array.isArray(entry.structuredRule.metadata.filesInvolved)
|
|
254
|
+
? entry.structuredRule.metadata.filesInvolved
|
|
255
|
+
: []),
|
|
256
|
+
entry.structuredRule && entry.structuredRule.action ? entry.structuredRule.action.description || '' : '',
|
|
257
|
+
entry.structuredRule && entry.structuredRule.trigger ? entry.structuredRule.trigger.condition || '' : '',
|
|
258
|
+
entry.richContext && entry.richContext.enforcement
|
|
259
|
+
? [
|
|
260
|
+
entry.richContext.enforcement.scopeViolation ? 'scope violation' : '',
|
|
261
|
+
entry.richContext.enforcement.approvalFailure ? 'approval failure' : '',
|
|
262
|
+
entry.richContext.enforcement.protectedFileViolation ? 'protected file violation' : '',
|
|
263
|
+
].join(' ')
|
|
264
|
+
: '',
|
|
239
265
|
].join(' ');
|
|
240
266
|
const norm = normalizePatternText(rawText);
|
|
241
267
|
if (!norm) continue;
|
|
@@ -260,6 +286,20 @@ function buildHybridState(opts) {
|
|
|
260
286
|
const rawText = [
|
|
261
287
|
entry.context || '',
|
|
262
288
|
entry.whatWentWrong || entry.what_went_wrong || '',
|
|
289
|
+
...(Array.isArray(entry.tags) ? entry.tags : []),
|
|
290
|
+
...(entry.richContext && Array.isArray(entry.richContext.filePaths) ? entry.richContext.filePaths : []),
|
|
291
|
+
...(entry.structuredRule && entry.structuredRule.metadata && Array.isArray(entry.structuredRule.metadata.filesInvolved)
|
|
292
|
+
? entry.structuredRule.metadata.filesInvolved
|
|
293
|
+
: []),
|
|
294
|
+
entry.structuredRule && entry.structuredRule.action ? entry.structuredRule.action.description || '' : '',
|
|
295
|
+
entry.structuredRule && entry.structuredRule.trigger ? entry.structuredRule.trigger.condition || '' : '',
|
|
296
|
+
entry.richContext && entry.richContext.enforcement
|
|
297
|
+
? [
|
|
298
|
+
entry.richContext.enforcement.scopeViolation ? 'scope violation' : '',
|
|
299
|
+
entry.richContext.enforcement.approvalFailure ? 'approval failure' : '',
|
|
300
|
+
entry.richContext.enforcement.protectedFileViolation ? 'protected file violation' : '',
|
|
301
|
+
].join(' ')
|
|
302
|
+
: '',
|
|
263
303
|
].join(' ');
|
|
264
304
|
const norm = normalizePatternText(rawText);
|
|
265
305
|
if (!norm) continue;
|
|
@@ -586,7 +626,7 @@ function evaluatePretool(toolName, toolInput, opts) {
|
|
|
586
626
|
const o = opts || {};
|
|
587
627
|
|
|
588
628
|
// Fast path: compiled artifact
|
|
589
|
-
const artifactPath = o.guardArtifactPath || process.env.THUMBGATE_GUARDS_PATH ||
|
|
629
|
+
const artifactPath = o.guardArtifactPath || process.env.THUMBGATE_GUARDS_PATH || getHybridPaths(o).guardArtifact;
|
|
590
630
|
const artifact = readGuardArtifact(artifactPath);
|
|
591
631
|
if (artifact) {
|
|
592
632
|
const result = evaluateCompiledGuards(artifact, toolName, toolInput);
|
|
@@ -667,6 +707,7 @@ module.exports = {
|
|
|
667
707
|
hashText,
|
|
668
708
|
hasTwoKeywordHits,
|
|
669
709
|
readJsonl,
|
|
710
|
+
getHybridPaths,
|
|
670
711
|
PATHS,
|
|
671
712
|
};
|
|
672
713
|
|
package/scripts/install-mcp.js
CHANGED
|
@@ -16,7 +16,8 @@ const fs = require('fs');
|
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const { resolveMcpEntry } = require('./mcp-config');
|
|
18
18
|
|
|
19
|
-
const MCP_SERVER_KEY = '
|
|
19
|
+
const MCP_SERVER_KEY = 'thumbgate';
|
|
20
|
+
const LEGACY_MCP_SERVER_KEYS = ['mcp-memory-gateway', 'rlhf'];
|
|
20
21
|
const PKG_ROOT = path.join(__dirname, '..');
|
|
21
22
|
const PKG_VERSION = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8')).version;
|
|
22
23
|
|
|
@@ -80,9 +81,15 @@ function serverConfigMatches(entry, flags = {}) {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
function isAlreadyInstalled(settings, flags = {}) {
|
|
84
|
+
const hasLegacyAliases = Boolean(
|
|
85
|
+
settings &&
|
|
86
|
+
settings.mcpServers &&
|
|
87
|
+
LEGACY_MCP_SERVER_KEYS.some((key) => Object.prototype.hasOwnProperty.call(settings.mcpServers, key))
|
|
88
|
+
);
|
|
83
89
|
return !!(
|
|
84
90
|
settings &&
|
|
85
91
|
settings.mcpServers &&
|
|
92
|
+
!hasLegacyAliases &&
|
|
86
93
|
serverConfigMatches(settings.mcpServers[MCP_SERVER_KEY], flags)
|
|
87
94
|
);
|
|
88
95
|
}
|
|
@@ -120,6 +127,11 @@ function installMcp(flags) {
|
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
settings.mcpServers[MCP_SERVER_KEY] = serverConfig;
|
|
130
|
+
for (const legacyKey of LEGACY_MCP_SERVER_KEYS) {
|
|
131
|
+
if (Object.prototype.hasOwnProperty.call(settings.mcpServers, legacyKey)) {
|
|
132
|
+
delete settings.mcpServers[legacyKey];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
123
135
|
|
|
124
136
|
// Ensure parent directory exists
|
|
125
137
|
const dir = path.dirname(settingsPath);
|
|
@@ -142,6 +154,7 @@ function installMcp(flags) {
|
|
|
142
154
|
// Exported for testing
|
|
143
155
|
module.exports = {
|
|
144
156
|
MCP_SERVER_KEY,
|
|
157
|
+
LEGACY_MCP_SERVER_KEYS,
|
|
145
158
|
MCP_SERVER_CONFIG,
|
|
146
159
|
resolveMcpServerConfig,
|
|
147
160
|
resolveSettingsPath,
|
package/scripts/intent-router.js
CHANGED
|
@@ -265,7 +265,7 @@ const ACTION_CATEGORY_MAP = {
|
|
|
265
265
|
feedback_summary: 'debugging',
|
|
266
266
|
search_lessons: 'search',
|
|
267
267
|
retrieve_lessons: 'search',
|
|
268
|
-
|
|
268
|
+
search_thumbgate: 'search',
|
|
269
269
|
prevention_rules: 'security',
|
|
270
270
|
construct_context_pack: 'architecture',
|
|
271
271
|
export_dpo_pairs: 'testing',
|
|
@@ -17,7 +17,7 @@ const { formatCodeGraphRecallSection } = require('./codegraph-context');
|
|
|
17
17
|
|
|
18
18
|
const KNOWN_SOURCES = new Set(['github', 'slack', 'linear', 'api', 'cli']);
|
|
19
19
|
const DEFAULT_SOURCE = 'api';
|
|
20
|
-
const DEFAULT_SANDBOX_ROOT = path.join(os.tmpdir(), '
|
|
20
|
+
const DEFAULT_SANDBOX_ROOT = path.join(os.tmpdir(), 'thumbgate-internal-agent-sandboxes');
|
|
21
21
|
|
|
22
22
|
function normalizeText(value) {
|
|
23
23
|
if (value === undefined || value === null) return '';
|
|
@@ -31,6 +31,11 @@ function getFeedbackDir() {
|
|
|
31
31
|
return resolveFeedbackDir();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function getLessonBaseUrl() {
|
|
35
|
+
const configuredOrigin = String(process.env.THUMBGATE_PUBLIC_APP_ORIGIN || '').trim().replace(/\/+$/, '');
|
|
36
|
+
return configuredOrigin || 'http://localhost:3456';
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
function getLessonsPath() { return path.join(getFeedbackDir(), LESSONS_FILE); }
|
|
35
40
|
function getRecentLessonPath() { return path.join(getFeedbackDir(), RECENT_LESSON_FILE); }
|
|
36
41
|
|
|
@@ -138,7 +143,7 @@ function createLesson({ feedbackId, signal, inferredLesson, triggerMessage, prio
|
|
|
138
143
|
};
|
|
139
144
|
|
|
140
145
|
// Stable link: dashboard deep-link to this lesson
|
|
141
|
-
lesson.link =
|
|
146
|
+
lesson.link = `${getLessonBaseUrl()}/lessons#${lesson.id}`;
|
|
142
147
|
|
|
143
148
|
const lessonsPath = getLessonsPath();
|
|
144
149
|
ensureDir(lessonsPath);
|