thumbgate 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/codex/config.toml +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/package.json +157 -9
- package/scripts/statusline.sh +1 -0
- package/src/api/server.js +113 -16
- package/src/index.js +3 -0
- package/.claude-plugin/bundle/icon.png +0 -0
- package/.claude-plugin/bundle/icon.svg +0 -18
- package/.claude-plugin/bundle/server/index.js +0 -24
- package/adapters/chatgpt/INSTALL.md +0 -158
- package/adapters/perplexity/.mcp.json +0 -36
- package/adapters/perplexity/config.toml +0 -16
- package/adapters/perplexity/opencode.json +0 -29
- package/bin/memory.sh +0 -64
- package/bin/obsidian-sync.sh +0 -20
- package/plugins/amp-skill/INSTALL.md +0 -52
- package/plugins/amp-skill/SKILL.md +0 -64
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +0 -22
- package/plugins/claude-codex-bridge/.mcp.json +0 -14
- package/plugins/claude-codex-bridge/INSTALL.md +0 -43
- package/plugins/claude-codex-bridge/README.md +0 -46
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +0 -286
- package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +0 -24
- package/plugins/claude-codex-bridge/skills/result/SKILL.md +0 -22
- package/plugins/claude-codex-bridge/skills/review/SKILL.md +0 -28
- package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +0 -27
- package/plugins/claude-codex-bridge/skills/setup/SKILL.md +0 -21
- package/plugins/claude-codex-bridge/skills/status/SKILL.md +0 -19
- package/plugins/claude-skill/INSTALL.md +0 -55
- package/plugins/claude-skill/SKILL.md +0 -46
- package/plugins/codex-profile/.codex-plugin/plugin.json +0 -43
- package/plugins/codex-profile/.mcp.json +0 -14
- package/plugins/codex-profile/AGENTS.md +0 -20
- package/plugins/codex-profile/INSTALL.md +0 -89
- package/plugins/codex-profile/README.md +0 -61
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +0 -23
- package/plugins/cursor-marketplace/CHANGELOG.md +0 -30
- package/plugins/cursor-marketplace/LICENSE +0 -21
- package/plugins/cursor-marketplace/README.md +0 -124
- package/plugins/cursor-marketplace/agents/reliability-reviewer.md +0 -31
- package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
- package/plugins/cursor-marketplace/commands/capture-feedback.md +0 -33
- package/plugins/cursor-marketplace/commands/check-gates.md +0 -25
- package/plugins/cursor-marketplace/commands/show-lessons.md +0 -27
- package/plugins/cursor-marketplace/hooks/hooks.json +0 -10
- package/plugins/cursor-marketplace/mcp.json +0 -14
- package/plugins/cursor-marketplace/rules/feedback-capture.mdc +0 -34
- package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +0 -30
- package/plugins/cursor-marketplace/rules/session-continuity.mdc +0 -28
- package/plugins/cursor-marketplace/scripts/gate-check.sh +0 -21
- package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +0 -48
- package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +0 -31
- package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +0 -30
- package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +0 -33
- package/plugins/gemini-extension/INSTALL.md +0 -92
- package/plugins/gemini-extension/gemini_prompt.txt +0 -14
- package/plugins/gemini-extension/tool_contract.json +0 -45
- package/plugins/opencode-profile/INSTALL.md +0 -57
- package/public/assets/instagram-card.png +0 -0
- package/public/assets/tiktok-agent-memory.mp4 +0 -0
- package/public/blog.html +0 -474
- package/public/compare/mem0.html +0 -189
- package/public/compare/speclock.html +0 -180
- package/public/compare.html +0 -310
- package/public/dashboard.html +0 -1100
- package/public/guide.html +0 -317
- package/public/guides/claude-code-prevent-repeated-mistakes.html +0 -161
- package/public/guides/codex-cli-guardrails.html +0 -158
- package/public/guides/cursor-prevent-repeated-mistakes.html +0 -161
- package/public/guides/pre-action-gates.html +0 -162
- package/public/guides/stop-repeated-ai-agent-mistakes.html +0 -159
- package/public/index.html +0 -1225
- package/public/js/buyer-intent.js +0 -252
- package/public/learn/agent-harness-pattern.html +0 -180
- package/public/learn/ai-agent-persistent-memory.html +0 -203
- package/public/learn/learn.css +0 -45
- package/public/learn/mcp-pre-action-gates-explained.html +0 -172
- package/public/learn/stop-ai-agent-force-push.html +0 -134
- package/public/learn/vibe-coding-safety-net.html +0 -142
- package/public/learn.html +0 -274
- package/public/lessons.html +0 -967
- package/public/llm-context.md +0 -156
- package/public/pro.html +0 -1087
- package/public/vercel.json +0 -8
- package/scripts/a2ui-engine.js +0 -73
- package/scripts/adk-consolidator.js +0 -274
- package/scripts/agent-security-hardening.js +0 -225
- package/scripts/ai-search-visibility.js +0 -116
- package/scripts/autonomous-sales-agent.js +0 -39
- package/scripts/autoresearch-runner.js +0 -216
- package/scripts/background-agent-governance.js +0 -229
- package/scripts/behavioral-extraction.js +0 -93
- package/scripts/budget-enforcer.js +0 -173
- package/scripts/budget-guard.js +0 -173
- package/scripts/build-claude-mcpb.js +0 -255
- package/scripts/build-codex-plugin.js +0 -152
- package/scripts/capture-railway-diagnostics.sh +0 -97
- package/scripts/changeset-check.js +0 -372
- package/scripts/check-congruence.js +0 -443
- package/scripts/computer-use-firewall.js +0 -280
- package/scripts/content-engine/linkedin-content-generator.js +0 -154
- package/scripts/content-engine/output/linkedin-memento-validation.md +0 -17
- package/scripts/content-engine/output/linkedin-posts-2026-04-09.md +0 -175
- package/scripts/content-engine/reddit-thread-finder.js +0 -154
- package/scripts/context-engine.js +0 -710
- package/scripts/daily-digest.js +0 -11
- package/scripts/data-governance.js +0 -173
- package/scripts/deploy-gcp.sh +0 -44
- package/scripts/deploy-policy.js +0 -249
- package/scripts/disagreement-mining.js +0 -315
- package/scripts/dpo-optimizer.js +0 -206
- package/scripts/ensure-repo-bootstrap.js +0 -130
- package/scripts/ephemeral-agent-store.js +0 -212
- package/scripts/eval-harness.js +0 -56
- package/scripts/export-kto-pairs.js +0 -309
- package/scripts/export-training.js +0 -446
- package/scripts/feedback-fallback.js +0 -111
- package/scripts/feedback-inbox-read.js +0 -162
- package/scripts/feedback-root-consolidator.js +0 -233
- package/scripts/feedback-to-memory.js +0 -185
- package/scripts/gate-satisfy.js +0 -42
- package/scripts/generate-paperbanana-diagrams.sh +0 -99
- package/scripts/generate-pretool-hook.sh +0 -40
- package/scripts/github-about.js +0 -430
- package/scripts/github-outreach.js +0 -65
- package/scripts/gtm-revenue-loop.js +0 -535
- package/scripts/hallucination-detector.js +0 -226
- package/scripts/hf-papers.js +0 -317
- package/scripts/hook-auto-capture.sh +0 -100
- package/scripts/hook-stop-pr-thread-check.sh +0 -68
- package/scripts/hook-stop-self-score.sh +0 -51
- package/scripts/hook-stop-verify-deploy.sh +0 -31
- package/scripts/hook-verify-before-done.sh +0 -20
- package/scripts/managed-dpo-export.js +0 -91
- package/scripts/markdown-escape.js +0 -12
- package/scripts/marketing-experiment.js +0 -657
- package/scripts/memalign-recall.js +0 -111
- package/scripts/memory-migration.js +0 -296
- package/scripts/meta-policy.js +0 -190
- package/scripts/metered-billing.js +0 -16
- package/scripts/model-tier-router.js +0 -310
- package/scripts/money-watcher.js +0 -218
- package/scripts/multi-hop-recall.js +0 -240
- package/scripts/per-step-scoring.js +0 -163
- package/scripts/perplexity-command-center.js +0 -644
- package/scripts/perplexity-marketing.js +0 -454
- package/scripts/pii-scanner.js +0 -153
- package/scripts/plan-gate.js +0 -154
- package/scripts/post-everywhere.js +0 -341
- package/scripts/post-to-x-retry.sh +0 -22
- package/scripts/post-to-x.js +0 -369
- package/scripts/pr-manager.js +0 -421
- package/scripts/principle-extractor.js +0 -162
- package/scripts/pro-features.js +0 -41
- package/scripts/prompt-dlp.js +0 -222
- package/scripts/prove-adapters.js +0 -860
- package/scripts/prove-attribution.js +0 -361
- package/scripts/prove-automation.js +0 -651
- package/scripts/prove-autoresearch.js +0 -304
- package/scripts/prove-claim-verification.js +0 -277
- package/scripts/prove-cloudflare-sandbox.js +0 -161
- package/scripts/prove-data-pipeline.js +0 -408
- package/scripts/prove-data-quality.js +0 -227
- package/scripts/prove-evolution.js +0 -352
- package/scripts/prove-harnesses.js +0 -287
- package/scripts/prove-intelligence.js +0 -257
- package/scripts/prove-lancedb.js +0 -425
- package/scripts/prove-local-intelligence.js +0 -340
- package/scripts/prove-loop-closure.js +0 -263
- package/scripts/prove-packaged-runtime.js +0 -327
- package/scripts/prove-predictive-insights.js +0 -355
- package/scripts/prove-runtime.js +0 -363
- package/scripts/prove-seo-gsd.js +0 -234
- package/scripts/prove-settings.js +0 -279
- package/scripts/prove-subway-upgrades.js +0 -277
- package/scripts/prove-tessl.js +0 -229
- package/scripts/prove-training-export.js +0 -325
- package/scripts/prove-workflow-contract.js +0 -112
- package/scripts/prove-xmemory.js +0 -332
- package/scripts/publish-decision.js +0 -159
- package/scripts/ralph-loop.js +0 -376
- package/scripts/ralph-mode-ci.js +0 -434
- package/scripts/reddit-dm-outreach.js +0 -192
- package/scripts/reddit-monitor-cron.sh +0 -26
- package/scripts/reminder-engine.js +0 -132
- package/scripts/revenue-status.js +0 -472
- package/scripts/rotate-stripe-webhook-secret.js +0 -314
- package/scripts/schedule-manager.js +0 -249
- package/scripts/self-healing-check.js +0 -193
- package/scripts/session-analyzer.js +0 -533
- package/scripts/shieldcortex-memory-firewall-runner.mjs +0 -53
- package/scripts/skill-exporter.js +0 -260
- package/scripts/skill-materializer.js +0 -134
- package/scripts/skill-packs.js +0 -136
- package/scripts/skill-proposer.js +0 -99
- package/scripts/skill-quality-tracker.js +0 -282
- package/scripts/slow-loop.js +0 -72
- package/scripts/social-analytics/db/marketing-db.js +0 -179
- package/scripts/social-analytics/db/schema.sql +0 -55
- package/scripts/social-analytics/digest.js +0 -256
- package/scripts/social-analytics/engagement-audit.js +0 -185
- package/scripts/social-analytics/generate-instagram-card.js +0 -123
- package/scripts/social-analytics/generate-slides.js +0 -268
- package/scripts/social-analytics/instagram-thumbgate-post.js +0 -111
- package/scripts/social-analytics/install-growth-automation.js +0 -114
- package/scripts/social-analytics/load-env.js +0 -77
- package/scripts/social-analytics/mcp-server.js +0 -289
- package/scripts/social-analytics/normalizer.js +0 -580
- package/scripts/social-analytics/notify.js +0 -162
- package/scripts/social-analytics/poll-all.js +0 -107
- package/scripts/social-analytics/pollers/github.js +0 -195
- package/scripts/social-analytics/pollers/instagram.js +0 -253
- package/scripts/social-analytics/pollers/linkedin.js +0 -340
- package/scripts/social-analytics/pollers/plausible.js +0 -245
- package/scripts/social-analytics/pollers/reddit.js +0 -306
- package/scripts/social-analytics/pollers/threads.js +0 -233
- package/scripts/social-analytics/pollers/tiktok.js +0 -203
- package/scripts/social-analytics/pollers/x.js +0 -227
- package/scripts/social-analytics/pollers/youtube.js +0 -304
- package/scripts/social-analytics/pollers/zernio.js +0 -183
- package/scripts/social-analytics/post-video.js +0 -316
- package/scripts/social-analytics/publish-instagram-thumbgate.js +0 -104
- package/scripts/social-analytics/publish-thumbgate-launch.js +0 -322
- package/scripts/social-analytics/publishers/devto.js +0 -122
- package/scripts/social-analytics/publishers/instagram.js +0 -317
- package/scripts/social-analytics/publishers/linkedin.js +0 -294
- package/scripts/social-analytics/publishers/reddit.js +0 -385
- package/scripts/social-analytics/publishers/threads.js +0 -275
- package/scripts/social-analytics/publishers/tiktok.js +0 -217
- package/scripts/social-analytics/publishers/x.js +0 -259
- package/scripts/social-analytics/publishers/youtube.js +0 -223
- package/scripts/social-analytics/publishers/zernio.js +0 -568
- package/scripts/social-analytics/reconcile-thumbgate-campaign.js +0 -165
- package/scripts/social-analytics/run-digest.js +0 -34
- package/scripts/social-analytics/schedule-thumbgate-campaign.js +0 -275
- package/scripts/social-analytics/store.js +0 -455
- package/scripts/social-analytics/sync-launch-assets.js +0 -185
- package/scripts/social-analytics/utm.js +0 -143
- package/scripts/social-pipeline.js +0 -2626
- package/scripts/social-post-hourly.js +0 -228
- package/scripts/social-quality-gate.js +0 -134
- package/scripts/social-reply-monitor.js +0 -592
- package/scripts/status-dashboard.js +0 -155
- package/scripts/stripe-live-status.js +0 -115
- package/scripts/subagent-profiles.js +0 -79
- package/scripts/sync-branch-protection.js +0 -340
- package/scripts/sync-gh-secrets-from-env.sh +0 -70
- package/scripts/sync-github-about.js +0 -55
- package/scripts/sync-version.js +0 -479
- package/scripts/synthetic-dpo.js +0 -234
- package/scripts/tessl-export.js +0 -369
- package/scripts/test-coverage.js +0 -128
- package/scripts/thumbgate-bench.js +0 -494
- package/scripts/thumbgate_session_start.sh +0 -32
- package/scripts/train_from_feedback.py +0 -929
- package/scripts/validate-feedback.js +0 -581
- package/scripts/verify-obsidian-setup.sh +0 -269
- package/scripts/verify-run.js +0 -269
- package/scripts/weekly-auto-post.js +0 -124
- package/scripts/x-autonomous-marketing.js +0 -139
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Disagreement Mining
|
|
4
|
-
*
|
|
5
|
-
* Inspired by Dropbox's "human-calibrated LLM labeling" approach.
|
|
6
|
-
* The strongest learning signal comes from cases where the Thompson
|
|
7
|
-
* Sampling model DISAGREES with actual user feedback signals. These
|
|
8
|
-
* disagreements are prioritized for review and DPO pair generation.
|
|
9
|
-
*
|
|
10
|
-
* Zero external npm dependencies.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
'use strict';
|
|
14
|
-
|
|
15
|
-
const path = require('path');
|
|
16
|
-
const { loadModel, getReliability } = require('./thompson-sampling');
|
|
17
|
-
const { readJSONL, getFeedbackPaths, inferDomain } = require('./feedback-loop');
|
|
18
|
-
const { extractDomainKeys } = require('./export-dpo-pairs');
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Domain → Thompson category mapping
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Map an inferDomain() result to the closest Thompson Sampling category.
|
|
26
|
-
* Thompson uses: code_edit, git, testing, pr_review, search, architecture,
|
|
27
|
-
* security, debugging, uncategorized.
|
|
28
|
-
*
|
|
29
|
-
* @param {string} domain - Domain from inferDomain()
|
|
30
|
-
* @returns {string} Thompson category key
|
|
31
|
-
*/
|
|
32
|
-
function domainToCategory(domain) {
|
|
33
|
-
const mapping = {
|
|
34
|
-
'testing': 'testing',
|
|
35
|
-
'security': 'security',
|
|
36
|
-
'debugging': 'debugging',
|
|
37
|
-
'architecture': 'architecture',
|
|
38
|
-
'git-workflow': 'git',
|
|
39
|
-
'ui-components': 'code_edit',
|
|
40
|
-
'api-integration': 'code_edit',
|
|
41
|
-
'documentation': 'uncategorized',
|
|
42
|
-
'data-modeling': 'architecture',
|
|
43
|
-
'performance': 'debugging',
|
|
44
|
-
'general': 'uncategorized',
|
|
45
|
-
};
|
|
46
|
-
return mapping[domain] || 'uncategorized';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Core: Mine Disagreements
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Find disagreements between Thompson model predictions and actual user signals.
|
|
55
|
-
*
|
|
56
|
-
* A "disagreement" is when:
|
|
57
|
-
* - Thompson reliability > highThreshold (e.g. 0.6) but user gave negative signal
|
|
58
|
-
* - Thompson reliability < lowThreshold (e.g. 0.4) but user gave positive signal
|
|
59
|
-
*
|
|
60
|
-
* @param {Object} [opts]
|
|
61
|
-
* @param {string} [opts.feedbackDir] - Override feedback dir
|
|
62
|
-
* @param {string} [opts.modelPath] - Override model path
|
|
63
|
-
* @param {number} [opts.highThreshold=0.6] - Reliability above which positive is expected
|
|
64
|
-
* @param {number} [opts.lowThreshold=0.4] - Reliability below which negative is expected
|
|
65
|
-
* @returns {{ disagreements: Array, stats: Object }}
|
|
66
|
-
*/
|
|
67
|
-
function mineDisagreements(opts) {
|
|
68
|
-
const options = opts || {};
|
|
69
|
-
const highThreshold = options.highThreshold != null ? options.highThreshold : 0.6;
|
|
70
|
-
const lowThreshold = options.lowThreshold != null ? options.lowThreshold : 0.4;
|
|
71
|
-
|
|
72
|
-
const paths = getFeedbackPaths();
|
|
73
|
-
const feedbackDir = options.feedbackDir || paths.FEEDBACK_DIR;
|
|
74
|
-
const modelPath = options.modelPath || path.join(feedbackDir, 'feedback_model.json');
|
|
75
|
-
const feedbackLogPath = path.join(feedbackDir, 'feedback-log.jsonl');
|
|
76
|
-
|
|
77
|
-
const model = loadModel(modelPath);
|
|
78
|
-
const reliability = getReliability(model);
|
|
79
|
-
const events = readJSONL(feedbackLogPath);
|
|
80
|
-
|
|
81
|
-
const disagreements = [];
|
|
82
|
-
|
|
83
|
-
for (let i = 0; i < events.length; i++) {
|
|
84
|
-
const event = events[i];
|
|
85
|
-
if (!event.signal) continue;
|
|
86
|
-
|
|
87
|
-
const isPositive = event.signal === 'positive';
|
|
88
|
-
const domain = inferDomain(event.tags, event.context);
|
|
89
|
-
const category = domainToCategory(domain);
|
|
90
|
-
const catReliability = reliability[category]
|
|
91
|
-
? reliability[category].reliability
|
|
92
|
-
: 0.5;
|
|
93
|
-
|
|
94
|
-
let disagreementType = null;
|
|
95
|
-
let strength = 0;
|
|
96
|
-
|
|
97
|
-
if (catReliability > highThreshold && !isPositive) {
|
|
98
|
-
// Model thinks this category is reliable, but user disagrees
|
|
99
|
-
disagreementType = 'model_overconfident';
|
|
100
|
-
strength = catReliability - highThreshold;
|
|
101
|
-
} else if (catReliability < lowThreshold && isPositive) {
|
|
102
|
-
// Model thinks this category is unreliable, but user approves
|
|
103
|
-
disagreementType = 'model_underconfident';
|
|
104
|
-
strength = lowThreshold - catReliability;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (disagreementType) {
|
|
108
|
-
disagreements.push({
|
|
109
|
-
feedbackIndex: i,
|
|
110
|
-
feedbackId: event.id || `event-${i}`,
|
|
111
|
-
signal: event.signal,
|
|
112
|
-
domain,
|
|
113
|
-
category,
|
|
114
|
-
categoryReliability: catReliability,
|
|
115
|
-
disagreementType,
|
|
116
|
-
disagreementStrength: Math.round(strength * 1000) / 1000,
|
|
117
|
-
context: event.context || '',
|
|
118
|
-
tags: event.tags || [],
|
|
119
|
-
timestamp: event.timestamp || null,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Sort by disagreement strength descending (strongest signal first)
|
|
125
|
-
disagreements.sort((a, b) => b.disagreementStrength - a.disagreementStrength);
|
|
126
|
-
|
|
127
|
-
const rate = events.length > 0
|
|
128
|
-
? Math.round((disagreements.length / events.length) * 1000) / 1000
|
|
129
|
-
: 0;
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
disagreements,
|
|
133
|
-
stats: {
|
|
134
|
-
totalEvents: events.length,
|
|
135
|
-
disagreementCount: disagreements.length,
|
|
136
|
-
disagreementRate: rate,
|
|
137
|
-
overconfident: disagreements.filter((d) => d.disagreementType === 'model_overconfident').length,
|
|
138
|
-
underconfident: disagreements.filter((d) => d.disagreementType === 'model_underconfident').length,
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
// Amplify: Generate DPO pairs from disagreements
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Generate amplified DPO pairs from disagreements.
|
|
149
|
-
* For each disagreement, synthesize a preference pair where:
|
|
150
|
-
* - chosen = what the user signal implies (corrective direction)
|
|
151
|
-
* - rejected = what the model predicted
|
|
152
|
-
*
|
|
153
|
-
* @param {Array} disagreements - From mineDisagreements()
|
|
154
|
-
* @returns {Array} DPO preference pairs with amplification metadata
|
|
155
|
-
*/
|
|
156
|
-
function amplifyFromDisagreements(disagreements) {
|
|
157
|
-
if (!Array.isArray(disagreements)) return [];
|
|
158
|
-
|
|
159
|
-
return disagreements.map((d) => {
|
|
160
|
-
const domainKeys = extractDomainKeys({
|
|
161
|
-
tags: d.tags,
|
|
162
|
-
title: d.context,
|
|
163
|
-
});
|
|
164
|
-
const domainLabel = domainKeys.length > 0 ? domainKeys.join(', ') : d.domain;
|
|
165
|
-
|
|
166
|
-
const prompt = `Domain: ${domainLabel}. ` +
|
|
167
|
-
`The agent performed a ${d.domain} task. ` +
|
|
168
|
-
(d.context ? `Context: ${d.context}` : 'How should the agent handle this scenario?');
|
|
169
|
-
|
|
170
|
-
let chosen;
|
|
171
|
-
let rejected;
|
|
172
|
-
|
|
173
|
-
if (d.disagreementType === 'model_overconfident') {
|
|
174
|
-
// Model thought it was good, user said no — user's correction is "chosen"
|
|
175
|
-
chosen = `The agent should NOT rely on its current ${d.domain} approach. ` +
|
|
176
|
-
`User feedback indicates failure despite model confidence of ${d.categoryReliability.toFixed(2)}. ` +
|
|
177
|
-
'Corrective action needed.';
|
|
178
|
-
rejected = `The agent's ${d.domain} approach is reliable ` +
|
|
179
|
-
`(model confidence: ${d.categoryReliability.toFixed(2)}). Continue current behavior.`;
|
|
180
|
-
} else {
|
|
181
|
-
// Model thought it was bad, user said it was fine — model is too cautious
|
|
182
|
-
chosen = `The agent's ${d.domain} approach succeeded. ` +
|
|
183
|
-
`User approved despite model skepticism (confidence: ${d.categoryReliability.toFixed(2)}). ` +
|
|
184
|
-
'This approach should be trusted more.';
|
|
185
|
-
rejected = `The agent's ${d.domain} approach is unreliable ` +
|
|
186
|
-
`(model confidence: ${d.categoryReliability.toFixed(2)}). Avoid this approach.`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
prompt,
|
|
191
|
-
chosen,
|
|
192
|
-
rejected,
|
|
193
|
-
metadata: {
|
|
194
|
-
disagreementStrength: d.disagreementStrength,
|
|
195
|
-
disagreementType: d.disagreementType,
|
|
196
|
-
domain: d.domain,
|
|
197
|
-
category: d.category,
|
|
198
|
-
originalFeedbackId: d.feedbackId,
|
|
199
|
-
amplified: true,
|
|
200
|
-
},
|
|
201
|
-
};
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// ---------------------------------------------------------------------------
|
|
206
|
-
// Calibrate: Cross-reference prevention rules with Thompson model
|
|
207
|
-
// ---------------------------------------------------------------------------
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Calibrate prevention rules by cross-referencing disagreements.
|
|
211
|
-
* Only promote rules where BOTH Thompson model AND user signals agree
|
|
212
|
-
* on the failure pattern (concordance check).
|
|
213
|
-
*
|
|
214
|
-
* @param {string} [feedbackDir] - Override feedback dir
|
|
215
|
-
* @returns {{ calibratedRules: Array, droppedRules: Array, concordanceRate: number }}
|
|
216
|
-
*/
|
|
217
|
-
function calibratePreventionRules(feedbackDir) {
|
|
218
|
-
const paths = getFeedbackPaths();
|
|
219
|
-
const dir = feedbackDir || paths.FEEDBACK_DIR;
|
|
220
|
-
const modelPath = path.join(dir, 'feedback_model.json');
|
|
221
|
-
const memoryLogPath = path.join(dir, 'memory-log.jsonl');
|
|
222
|
-
|
|
223
|
-
const model = loadModel(modelPath);
|
|
224
|
-
const reliability = getReliability(model);
|
|
225
|
-
const memories = readJSONL(memoryLogPath);
|
|
226
|
-
|
|
227
|
-
// Filter to error memories only
|
|
228
|
-
const errorMemories = memories.filter((m) => m.category === 'error');
|
|
229
|
-
|
|
230
|
-
const calibratedRules = [];
|
|
231
|
-
const droppedRules = [];
|
|
232
|
-
|
|
233
|
-
for (const mem of errorMemories) {
|
|
234
|
-
const domain = inferDomain(mem.tags, mem.content || mem.title || '');
|
|
235
|
-
const category = domainToCategory(domain);
|
|
236
|
-
const catReliability = reliability[category]
|
|
237
|
-
? reliability[category].reliability
|
|
238
|
-
: 0.5;
|
|
239
|
-
|
|
240
|
-
const rule = {
|
|
241
|
-
domain,
|
|
242
|
-
category,
|
|
243
|
-
title: mem.title || '',
|
|
244
|
-
categoryReliability: catReliability,
|
|
245
|
-
memoryId: mem.id || null,
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
// Concordance: both user (error memory) and Thompson (low reliability) agree
|
|
249
|
-
if (catReliability < 0.5) {
|
|
250
|
-
calibratedRules.push(rule);
|
|
251
|
-
} else {
|
|
252
|
-
droppedRules.push(rule);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const total = calibratedRules.length + droppedRules.length;
|
|
257
|
-
const concordanceRate = total > 0
|
|
258
|
-
? Math.round((calibratedRules.length / total) * 1000) / 1000
|
|
259
|
-
: 1;
|
|
260
|
-
|
|
261
|
-
return {
|
|
262
|
-
calibratedRules,
|
|
263
|
-
droppedRules,
|
|
264
|
-
concordanceRate,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// ---------------------------------------------------------------------------
|
|
269
|
-
// CLI
|
|
270
|
-
// ---------------------------------------------------------------------------
|
|
271
|
-
|
|
272
|
-
function parseArgs(argv) {
|
|
273
|
-
const args = {};
|
|
274
|
-
argv.forEach((arg) => {
|
|
275
|
-
if (!arg.startsWith('--')) return;
|
|
276
|
-
const [key, ...rest] = arg.slice(2).split('=');
|
|
277
|
-
args[key] = rest.length ? rest.join('=') : true;
|
|
278
|
-
});
|
|
279
|
-
return args;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (require.main === module) {
|
|
283
|
-
const args = parseArgs(process.argv.slice(2));
|
|
284
|
-
|
|
285
|
-
if (args.calibrate) {
|
|
286
|
-
const result = calibratePreventionRules(args['feedback-dir']);
|
|
287
|
-
console.log(JSON.stringify(result, null, 2));
|
|
288
|
-
} else {
|
|
289
|
-
// --mine or --amplify (amplify implies mine)
|
|
290
|
-
const result = mineDisagreements({
|
|
291
|
-
feedbackDir: args['feedback-dir'],
|
|
292
|
-
modelPath: args['model-path'],
|
|
293
|
-
highThreshold: args['high-threshold'] ? Number(args['high-threshold']) : undefined,
|
|
294
|
-
lowThreshold: args['low-threshold'] ? Number(args['low-threshold']) : undefined,
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
if (args.amplify) {
|
|
298
|
-
const pairs = amplifyFromDisagreements(result.disagreements);
|
|
299
|
-
console.log(JSON.stringify({ pairs, stats: result.stats }, null, 2));
|
|
300
|
-
} else {
|
|
301
|
-
console.log(JSON.stringify(result, null, 2));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// ---------------------------------------------------------------------------
|
|
307
|
-
// Exports
|
|
308
|
-
// ---------------------------------------------------------------------------
|
|
309
|
-
|
|
310
|
-
module.exports = {
|
|
311
|
-
mineDisagreements,
|
|
312
|
-
amplifyFromDisagreements,
|
|
313
|
-
calibratePreventionRules,
|
|
314
|
-
domainToCategory,
|
|
315
|
-
};
|
package/scripts/dpo-optimizer.js
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/**
|
|
3
|
-
* DPO Batch Optimizer (DPO-02)
|
|
4
|
-
*
|
|
5
|
-
* Builds (chosen, rejected) preference pairs from feedback-log.jsonl memories,
|
|
6
|
-
* computes DPO log-ratio adjustments using Thompson Sampling posteriors,
|
|
7
|
-
* and writes dpo-model.json to THUMBGATE_FEEDBACK_DIR.
|
|
8
|
-
*
|
|
9
|
-
* Does NOT call any external API. Pure offline batch optimization.
|
|
10
|
-
*
|
|
11
|
-
* Exports: run, buildPreferencePairs, applyDpoAdjustments, dpoLogRatio
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const fs = require('fs');
|
|
15
|
-
const path = require('path');
|
|
16
|
-
const { getEffectiveSetting } = require('./evolution-state');
|
|
17
|
-
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
18
|
-
|
|
19
|
-
const DPO_BETA = 0.1;
|
|
20
|
-
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// dpoLogRatio — DPO preference adjustment formula
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Compute DPO adjustment in range [-1, +1] from chosen and rejected weights.
|
|
27
|
-
*
|
|
28
|
-
* @param {number} chosenWeight - Time-decay weight for chosen (positive) outcome
|
|
29
|
-
* @param {number} rejectedWeight - Time-decay weight for rejected (negative) outcome
|
|
30
|
-
* @param {number} [beta=0.1] - Temperature parameter (lower = more aggressive)
|
|
31
|
-
* @returns {number} Adjustment in [-1, +1]
|
|
32
|
-
*/
|
|
33
|
-
function dpoLogRatio(chosenWeight, rejectedWeight, beta) {
|
|
34
|
-
const b = (beta !== undefined && beta !== null) ? beta : getEffectiveSetting('dpo_beta', DPO_BETA);
|
|
35
|
-
const cw = Math.max(chosenWeight, 0.01);
|
|
36
|
-
const rw = Math.max(rejectedWeight, 0.01);
|
|
37
|
-
const logRatio = Math.log(cw) - Math.log(rw);
|
|
38
|
-
const sigmoid = 1.0 / (1.0 + Math.exp(-b * logRatio));
|
|
39
|
-
return (sigmoid - 0.5) * 2;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// buildPreferencePairs — groups DPO pairs by category from feedbackDir
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Build preference pairs grouped by category.
|
|
48
|
-
*
|
|
49
|
-
* Uses buildDpoPairs() from export-dpo-pairs.js (do NOT reimplement).
|
|
50
|
-
* Reads memory-log.jsonl from feedbackDir to get error + learning memories.
|
|
51
|
-
*
|
|
52
|
-
* @param {string} feedbackDir - Directory containing memory-log.jsonl
|
|
53
|
-
* @returns {Object} Map of category → [{ chosen, rejected }]
|
|
54
|
-
*/
|
|
55
|
-
function buildPreferencePairs(feedbackDir) {
|
|
56
|
-
const { buildDpoPairs, readJSONL } = require('./export-dpo-pairs');
|
|
57
|
-
const memoryLogPath = path.join(feedbackDir, 'memory-log.jsonl');
|
|
58
|
-
const memories = readJSONL(memoryLogPath);
|
|
59
|
-
|
|
60
|
-
const errors = memories.filter((m) => m.category === 'error');
|
|
61
|
-
const learnings = memories.filter((m) => m.category === 'learning');
|
|
62
|
-
|
|
63
|
-
const result = buildDpoPairs(errors, learnings);
|
|
64
|
-
|
|
65
|
-
// Group pairs by category (inferred from matchedKeys or tags)
|
|
66
|
-
const grouped = {};
|
|
67
|
-
for (const pair of result.pairs) {
|
|
68
|
-
const keys = (pair.metadata && pair.metadata.matchedKeys) || [];
|
|
69
|
-
const category = keys.length > 0 ? keys[0] : 'uncategorized';
|
|
70
|
-
if (!grouped[category]) grouped[category] = [];
|
|
71
|
-
grouped[category].push({
|
|
72
|
-
chosen: pair.metadata
|
|
73
|
-
? { id: pair.metadata.learningId, content: pair.chosen, timestamp: null }
|
|
74
|
-
: { content: pair.chosen },
|
|
75
|
-
rejected: pair.metadata
|
|
76
|
-
? { id: pair.metadata.errorId, content: pair.rejected, timestamp: null }
|
|
77
|
-
: { content: pair.rejected },
|
|
78
|
-
metadata: pair.metadata,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return grouped;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
|
-
// applyDpoAdjustments — mutates Thompson model with DPO posterior adjustments
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Apply DPO adjustments to Thompson Sampling posteriors and save the model.
|
|
91
|
-
*
|
|
92
|
-
* CRITICAL: calls ts.saveModel(model, modelPath) after all mutations.
|
|
93
|
-
*
|
|
94
|
-
* @param {string} modelPath - Path to feedback_model.json
|
|
95
|
-
* @param {Object} pairs - Map of category → [{ chosen, rejected }] from buildPreferencePairs
|
|
96
|
-
* @returns {Object} adjustments - Map of category → { pairs, avg_adjustment }
|
|
97
|
-
*/
|
|
98
|
-
function applyDpoAdjustments(modelPath, pairs) {
|
|
99
|
-
const ts = require('./thompson-sampling');
|
|
100
|
-
const model = ts.loadModel(modelPath);
|
|
101
|
-
const adjustments = {};
|
|
102
|
-
|
|
103
|
-
for (const [cat, catPairs] of Object.entries(pairs)) {
|
|
104
|
-
if (!catPairs || catPairs.length === 0) continue;
|
|
105
|
-
|
|
106
|
-
// Ensure category exists in model
|
|
107
|
-
if (!model.categories[cat]) {
|
|
108
|
-
model.categories[cat] = { alpha: 1.0, beta: 1.0, samples: 0, last_updated: null };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
let total = 0;
|
|
112
|
-
for (const pair of catPairs) {
|
|
113
|
-
const chosenTs = (pair.chosen && pair.chosen.timestamp) || null;
|
|
114
|
-
const rejectedTs = (pair.rejected && pair.rejected.timestamp) || null;
|
|
115
|
-
const cw = ts.timeDecayWeight(chosenTs);
|
|
116
|
-
const rw = ts.timeDecayWeight(rejectedTs);
|
|
117
|
-
total += dpoLogRatio(cw, rw);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const avg = total / catPairs.length;
|
|
121
|
-
|
|
122
|
-
if (avg > 0) {
|
|
123
|
-
model.categories[cat].alpha += avg * catPairs.length * 0.5;
|
|
124
|
-
} else {
|
|
125
|
-
model.categories[cat].beta += Math.abs(avg) * catPairs.length * 0.5;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
adjustments[cat] = {
|
|
129
|
-
pairs: catPairs.length,
|
|
130
|
-
avg_adjustment: Math.round(avg * 10000) / 10000,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// CRITICAL: save after all mutations (Pitfall 2 from RESEARCH.md)
|
|
135
|
-
ts.saveModel(model, modelPath);
|
|
136
|
-
|
|
137
|
-
return adjustments;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ---------------------------------------------------------------------------
|
|
141
|
-
// run — top-level batch DPO optimization entry point
|
|
142
|
-
// ---------------------------------------------------------------------------
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Run the full DPO optimization batch:
|
|
146
|
-
* 1. Build preference pairs from memory-log.jsonl
|
|
147
|
-
* 2. Apply DPO adjustments to Thompson model
|
|
148
|
-
* 3. Write dpo-model.json to feedbackDir
|
|
149
|
-
*
|
|
150
|
-
* @param {Object} [opts]
|
|
151
|
-
* @param {string} [opts.feedbackDir] - Override THUMBGATE_FEEDBACK_DIR
|
|
152
|
-
* @param {string} [opts.modelPath] - Override Thompson model path
|
|
153
|
-
* @returns {{ adjustments: Object, pairs_processed: number }}
|
|
154
|
-
*/
|
|
155
|
-
function run(opts) {
|
|
156
|
-
const options = opts || {};
|
|
157
|
-
const feedbackDir = options.feedbackDir || resolveFeedbackDir();
|
|
158
|
-
const modelPath = options.modelPath || path.join(feedbackDir, 'feedback_model.json');
|
|
159
|
-
|
|
160
|
-
const pairs = buildPreferencePairs(feedbackDir);
|
|
161
|
-
|
|
162
|
-
const pairsProcessed = Object.values(pairs).reduce((sum, arr) => sum + arr.length, 0);
|
|
163
|
-
|
|
164
|
-
let adjustments = {};
|
|
165
|
-
if (pairsProcessed > 0) {
|
|
166
|
-
adjustments = applyDpoAdjustments(modelPath, pairs);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const dpoModel = {
|
|
170
|
-
generated: new Date().toISOString(),
|
|
171
|
-
pairs_processed: pairsProcessed,
|
|
172
|
-
adjustments,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const dpoModelPath = path.join(feedbackDir, 'dpo-model.json');
|
|
176
|
-
if (!fs.existsSync(feedbackDir)) {
|
|
177
|
-
fs.mkdirSync(feedbackDir, { recursive: true });
|
|
178
|
-
}
|
|
179
|
-
fs.writeFileSync(dpoModelPath, `${JSON.stringify(dpoModel, null, 2)}\n`);
|
|
180
|
-
|
|
181
|
-
console.log(`DPO optimization complete: ${pairsProcessed} pairs processed`);
|
|
182
|
-
if (Object.keys(adjustments).length > 0) {
|
|
183
|
-
console.log('Adjustments:', JSON.stringify(adjustments, null, 2));
|
|
184
|
-
} else {
|
|
185
|
-
console.log('No adjustment pairs found (empty or no overlapping memories)');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return { adjustments, pairs_processed: pairsProcessed };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// ---------------------------------------------------------------------------
|
|
192
|
-
// CLI entry point
|
|
193
|
-
// ---------------------------------------------------------------------------
|
|
194
|
-
|
|
195
|
-
if (require.main === module && process.argv.includes('--run')) {
|
|
196
|
-
run().catch((e) => {
|
|
197
|
-
console.error(e);
|
|
198
|
-
process.exit(1);
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ---------------------------------------------------------------------------
|
|
203
|
-
// Exports
|
|
204
|
-
// ---------------------------------------------------------------------------
|
|
205
|
-
|
|
206
|
-
module.exports = { run, buildPreferencePairs, applyDpoAdjustments, dpoLogRatio };
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
const REPO_ROOT = path.resolve(process.argv[2] || process.cwd());
|
|
8
|
-
const THUMBGATE_ENTRY = {
|
|
9
|
-
command: 'npx',
|
|
10
|
-
args: ['-y', 'thumbgate@latest', 'serve'],
|
|
11
|
-
};
|
|
12
|
-
const MCP_SERVER_KEY = 'thumbgate';
|
|
13
|
-
const LEGACY_SERVER_NAMES = ['rlhf', 'mcp-memory-gateway', 'rlhf_feedback_loop'];
|
|
14
|
-
const INFO_EXCLUDE_ENTRIES = ['.thumbgate/', '.mcp.json'];
|
|
15
|
-
|
|
16
|
-
function readJson(filePath) {
|
|
17
|
-
try {
|
|
18
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function writeJsonIfChanged(filePath, value) {
|
|
25
|
-
const next = JSON.stringify(value, null, 2) + '\n';
|
|
26
|
-
let current = null;
|
|
27
|
-
try {
|
|
28
|
-
current = fs.readFileSync(filePath, 'utf8');
|
|
29
|
-
} catch {
|
|
30
|
-
current = null;
|
|
31
|
-
}
|
|
32
|
-
if (current === next) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
36
|
-
fs.writeFileSync(filePath, next);
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function mergeThumbgateEntry(entry = {}) {
|
|
41
|
-
return {
|
|
42
|
-
...entry,
|
|
43
|
-
command: THUMBGATE_ENTRY.command,
|
|
44
|
-
args: THUMBGATE_ENTRY.args.slice(),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function ensureMcpJson(repoRoot) {
|
|
49
|
-
const filePath = path.join(repoRoot, '.mcp.json');
|
|
50
|
-
const existing = readJson(filePath);
|
|
51
|
-
const config = existing && typeof existing === 'object' ? existing : {};
|
|
52
|
-
config.mcpServers = config.mcpServers && typeof config.mcpServers === 'object' ? config.mcpServers : {};
|
|
53
|
-
config.mcpServers[MCP_SERVER_KEY] = mergeThumbgateEntry(config.mcpServers[MCP_SERVER_KEY]);
|
|
54
|
-
for (const legacyName of LEGACY_SERVER_NAMES) {
|
|
55
|
-
delete config.mcpServers[legacyName];
|
|
56
|
-
}
|
|
57
|
-
return writeJsonIfChanged(filePath, config);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function ensureClaudeSettings(repoRoot) {
|
|
61
|
-
const filePath = path.join(repoRoot, '.claude', 'settings.json');
|
|
62
|
-
const existing = readJson(filePath);
|
|
63
|
-
if (!existing || typeof existing !== 'object') {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
const hasRelevantServer =
|
|
67
|
-
Boolean(existing.mcpServers && existing.mcpServers[MCP_SERVER_KEY]) ||
|
|
68
|
-
LEGACY_SERVER_NAMES.some((name) => Boolean(existing.mcpServers && existing.mcpServers[name]));
|
|
69
|
-
if (!hasRelevantServer) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
existing.mcpServers = existing.mcpServers && typeof existing.mcpServers === 'object' ? existing.mcpServers : {};
|
|
73
|
-
existing.mcpServers[MCP_SERVER_KEY] = mergeThumbgateEntry(existing.mcpServers[MCP_SERVER_KEY]);
|
|
74
|
-
for (const legacyName of LEGACY_SERVER_NAMES) {
|
|
75
|
-
delete existing.mcpServers[legacyName];
|
|
76
|
-
}
|
|
77
|
-
return writeJsonIfChanged(filePath, existing);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function ensureInfoExclude(repoRoot) {
|
|
81
|
-
const excludePath = path.join(repoRoot, '.git', 'info', 'exclude');
|
|
82
|
-
let current = '';
|
|
83
|
-
try {
|
|
84
|
-
current = fs.readFileSync(excludePath, 'utf8');
|
|
85
|
-
} catch {
|
|
86
|
-
current = '';
|
|
87
|
-
}
|
|
88
|
-
const lines = new Set(
|
|
89
|
-
current
|
|
90
|
-
.split(/\r?\n/)
|
|
91
|
-
.map((line) => line.trim())
|
|
92
|
-
.filter(Boolean)
|
|
93
|
-
);
|
|
94
|
-
let changed = false;
|
|
95
|
-
for (const entry of INFO_EXCLUDE_ENTRIES) {
|
|
96
|
-
if (!lines.has(entry)) {
|
|
97
|
-
lines.add(entry);
|
|
98
|
-
changed = true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (!changed) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
const next = `${Array.from(lines).sort().join('\n')}\n`;
|
|
105
|
-
fs.mkdirSync(path.dirname(excludePath), { recursive: true });
|
|
106
|
-
fs.writeFileSync(excludePath, next);
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function ensureThumbgateDir(repoRoot) {
|
|
111
|
-
const thumbgateDir = path.join(repoRoot, '.thumbgate');
|
|
112
|
-
if (fs.existsSync(thumbgateDir)) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
fs.mkdirSync(thumbgateDir, { recursive: true });
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function main() {
|
|
120
|
-
const results = {
|
|
121
|
-
repoRoot: REPO_ROOT,
|
|
122
|
-
createdThumbgateDir: ensureThumbgateDir(REPO_ROOT),
|
|
123
|
-
updatedMcpJson: ensureMcpJson(REPO_ROOT),
|
|
124
|
-
updatedClaudeSettings: ensureClaudeSettings(REPO_ROOT),
|
|
125
|
-
updatedInfoExclude: ensureInfoExclude(REPO_ROOT),
|
|
126
|
-
};
|
|
127
|
-
process.stdout.write(`${JSON.stringify(results)}\n`);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
main();
|