thumbgate 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/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 +10 -1
- package/adapters/opencode/opencode.json +1 -1
- package/config/mcp-allowlists.json +1 -0
- package/package.json +4 -2
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-codex-bridge/.mcp.json +1 -1
- package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-profile/.mcp.json +1 -1
- package/plugins/codex-profile/INSTALL.md +1 -1
- package/plugins/codex-profile/README.md +1 -1
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
- package/plugins/opencode-profile/INSTALL.md +1 -1
- package/public/index.html +7 -3
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/export-hf-dataset.js +293 -0
- package/scripts/tool-registry.js +11 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
3
|
"description": "Pre-action gates that block AI coding agents from repeating known mistakes. Captures feedback, auto-promotes failures into prevention rules, and enforces them via PreToolUse hooks.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "ThumbGate — 👍👎 feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
|
|
5
5
|
"homepage": "https://github.com/IgorGanapolsky/thumbgate",
|
|
6
6
|
"transport": "stdio",
|
package/adapters/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- `chatgpt/openapi.yaml`: import into GPT Actions.
|
|
4
4
|
- `gemini/function-declarations.json`: Gemini function-calling definitions.
|
|
5
5
|
- `mcp/server-stdio.js`: underlying local MCP stdio server implementation.
|
|
6
|
-
- `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.
|
|
6
|
+
- `claude/.mcp.json`: example Claude Code MCP config using `npx --yes --package thumbgate@1.1.0 thumbgate serve`.
|
|
7
7
|
- `codex/config.toml`: example Codex MCP profile section using the same version-pinned portable launcher.
|
|
8
8
|
- `amp/skills/thumbgate-feedback/SKILL.md`: Amp skill template.
|
|
9
9
|
- `opencode/opencode.json`: portable OpenCode MCP profile using the same version-pinned portable launcher.
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"thumbgate": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--yes", "--package", "thumbgate@1.
|
|
5
|
+
"args": ["--yes", "--package", "thumbgate@1.1.0", "thumbgate", "serve"]
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
"hooks": {
|
|
9
9
|
"preToolUse": {
|
|
10
10
|
"command": "npx",
|
|
11
|
-
"args": ["--yes", "--package", "thumbgate@1.
|
|
11
|
+
"args": ["--yes", "--package", "thumbgate@1.1.0", "thumbgate", "gate-check"]
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Codex MCP profile (copy into ~/.codex/config.toml or merge section)
|
|
2
2
|
[mcp_servers.thumbgate]
|
|
3
3
|
command = "npx"
|
|
4
|
-
args = ["--yes", "--package", "thumbgate@1.
|
|
4
|
+
args = ["--yes", "--package", "thumbgate@1.1.0", "thumbgate", "serve"]
|
|
5
5
|
|
|
6
6
|
# Hard PreToolUse hook for Codex
|
|
7
7
|
[hooks.pre_tool_use]
|
|
8
8
|
command = "npx"
|
|
9
|
-
args = ["--yes", "--package", "thumbgate@1.
|
|
9
|
+
args = ["--yes", "--package", "thumbgate@1.1.0", "thumbgate", "gate-check"]
|
|
@@ -97,6 +97,7 @@ const {
|
|
|
97
97
|
assembleUnifiedContext,
|
|
98
98
|
formatUnifiedContext,
|
|
99
99
|
} = require('../../scripts/context-manager');
|
|
100
|
+
const { exportHfDataset } = require('../../scripts/export-hf-dataset');
|
|
100
101
|
|
|
101
102
|
const PRO_CHECKOUT_URL = 'https://thumbgate-production.up.railway.app/checkout/pro';
|
|
102
103
|
|
|
@@ -118,7 +119,7 @@ const {
|
|
|
118
119
|
finalizeSession: finalizeFeedbackSession,
|
|
119
120
|
} = require('../../scripts/feedback-session');
|
|
120
121
|
|
|
121
|
-
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.
|
|
122
|
+
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.1.0' };
|
|
122
123
|
const COMMERCE_CATEGORIES = [
|
|
123
124
|
'product_recommendation',
|
|
124
125
|
'brand_compliance',
|
|
@@ -493,6 +494,14 @@ async function callToolInner(name, args) {
|
|
|
493
494
|
case 'export_dpo_pairs':
|
|
494
495
|
enforceLimit('export_dpo');
|
|
495
496
|
return buildExportDpoResponse(args);
|
|
497
|
+
case 'export_hf_dataset': {
|
|
498
|
+
enforceLimit('export_dpo');
|
|
499
|
+
const outputDir = args.outputDir ? resolveSafePath(args.outputDir) : undefined;
|
|
500
|
+
return toTextResult(exportHfDataset({
|
|
501
|
+
outputDir,
|
|
502
|
+
includeProvenance: args.includeProvenance !== false,
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
496
505
|
case 'export_databricks_bundle': {
|
|
497
506
|
enforceLimit('export_databricks');
|
|
498
507
|
const outputPath = args.outputPath ? resolveSafePath(args.outputPath) : undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "ThumbGate — Make your AI coding agent self-improving. Every mistake becomes a prevention rule that physically blocks the agent from repeating it. Feedback-driven enforcement via PreToolUse hooks, Thompson Sampling for adaptive gates, SQLite+FTS5 lesson DB, and LanceDB vector search. Your agent gets smarter with every session.",
|
|
5
5
|
"homepage": "https://thumbgate-production.up.railway.app",
|
|
6
6
|
"repository": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"social:post-everywhere:dry": "node scripts/post-everywhere.js --dry-run",
|
|
67
67
|
"social:reply-monitor": "node scripts/social-reply-monitor.js",
|
|
68
68
|
"social:reply-monitor:dry": "node scripts/social-reply-monitor.js --dry-run",
|
|
69
|
-
"test": "npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:zernio && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:check-congruence && npm run test:tool-registry && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:belief-update && npm run test:hosted-config && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:plan-gate && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:export-dpo-pairs && npm run test:license && npm run test:bot-detector && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:lesson-retrieval && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets",
|
|
69
|
+
"test": "npm run test:schema && npm run test:loop && npm run test:dpo && npm run test:kto && npm run test:api && npm run test:proof && npm run test:e2e && npm run test:rlaif && npm run test:attribution && npm run test:quality && npm run test:intelligence && npm run test:training-export && npm run test:deployment && npm run test:operational-integrity && npm run test:workflow && npm run test:billing && npm run test:cli && npm run test:watcher && npm run test:autoresearch && npm run test:ops && npm run test:tessl && npm run test:gates && npm run test:evoskill && npm run test:gates-hardening && npm run test:workers && npm run test:social-analytics && npm run test:memalign && npm run test:xmemory-lite && npm run test:filesystem-search && npm run test:zernio && npm run test:obsidian-export && npm run test:lesson-db && npm run test:lesson-rotation && npm run test:memory-dedup && npm run test:feedback-quality && npm run test:sync-version && npm run test:check-congruence && npm run test:tool-registry && npm run test:feedback-to-rules && npm run test:memory-firewall && npm run test:belief-update && npm run test:hosted-config && npm run test:cloudflare-sandbox && npm run test:mcp-config && npm run test:plan-gate && npm run test:pulse && npm run test:semantic-layer && npm run test:data-pipeline && npm run test:optimize-context && npm run test:principle-extractor && npm run test:analytics-window && npm run test:funnel-analytics && npm run test:experiment-tracker && npm run test:build-metadata && npm run test:context-engine && npm run test:hf-papers && npm run test:marketing-experiment && npm run test:seo-gsd && npm run test:verify-run && npm run test:export-dpo-pairs && npm run test:export-hf-dataset && npm run test:license && npm run test:bot-detector && npm run test:postinstall && npm run test:funnel-invariants && npm run test:cli-telemetry && npm run test:pro-parity && npm run test:model-tier-router && npm run test:computer-use-firewall && npm run test:skill-exporter && npm run test:statusline && npm run test:evolution && npm run test:org-dashboard && npm run test:multi-hop-recall && npm run test:synthetic-dpo && npm run test:thumbgate-skill && npm run test:learn-hub && npm run test:feedback-fallback && npm run test:metaclaw && npm run test:server-lock && npm run test:control-tower && npm run test:pii-scanner && npm run test:data-governance && npm run test:lesson-inference && npm run test:lesson-retrieval && npm run test:reflector-agent && npm run test:feedback-session && npm run test:feedback-history-distiller && npm run test:hallucination-detector && npm run test:history-distiller && npm run test:predictive-insights && npm run test:prove-predictive-insights && npm run test:statusbar-cli && npm run test:generate-instagram-card && npm run test:instagram-thumbgate-post && npm run test:publish-instagram-thumbgate && npm run test:lesson-synthesis && npm run test:background-governance && npm run test:memory-migration && npm run test:prompt-dlp && npm run test:ephemeral-store && npm run test:agent-security && npm run test:skill-progressive && npm run test:per-step-scoring && npm run test:weekly-auto-post && npm run test:social-quality-gate && npm run test:a2ui-engine && npm run test:gate-satisfy && npm run test:money-watcher && npm run test:utm && npm run test:product-feedback && npm run test:feedback-root-consolidator && npm run test:engagement-audit && npm run test:install-growth-automation && npm run test:publish-thumbgate-launch && npm run test:reconcile-thumbgate-campaign && npm run test:reddit-publisher && npm run test:schedule-thumbgate-campaign && npm run test:social-reply-monitor && npm run test:sync-launch-assets",
|
|
70
70
|
"test:feedback-fallback": "node --test tests/feedback-fallback.test.js",
|
|
71
71
|
"test:metaclaw": "node --test tests/metaclaw-features.test.js",
|
|
72
72
|
"test:server-lock": "node --test tests/server-stdio-lock.test.js",
|
|
@@ -105,6 +105,8 @@
|
|
|
105
105
|
"test:seo-gsd": "node --test tests/seo-gsd.test.js",
|
|
106
106
|
"test:verify-run": "node --test tests/verify-run.test.js",
|
|
107
107
|
"test:export-dpo-pairs": "node --test tests/export-dpo-pairs.test.js",
|
|
108
|
+
"test:export-hf-dataset": "node --test tests/export-hf-dataset.test.js",
|
|
109
|
+
"export:hf": "node scripts/export-hf-dataset.js",
|
|
108
110
|
"seo:gsd": "node scripts/seo-gsd.js plan",
|
|
109
111
|
"seo:gsd:write": "node scripts/seo-gsd.js plan --write",
|
|
110
112
|
"test:congruence": "node scripts/check-congruence.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Run Codex review, adversarial review, and second-pass handoffs from Claude Code while keeping ThumbGate reliability memory in the loop.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-profile",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "ThumbGate for Codex: pre-action gates, skill packs, hallucination detection, PII scanning, progressive disclosure (82% token savings), and MCP-backed reliability memory.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky",
|
|
@@ -31,7 +31,7 @@ The following block is appended to `~/.codex/config.toml`:
|
|
|
31
31
|
```toml
|
|
32
32
|
[mcp_servers.thumbgate]
|
|
33
33
|
command = "npx"
|
|
34
|
-
args = ["--yes", "--package", "thumbgate@1.
|
|
34
|
+
args = ["--yes", "--package", "thumbgate@1.1.0", "thumbgate", "serve"]
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
The repo-local Codex app plugin ships the same runtime path through `plugins/codex-profile/.mcp.json`, so the manual config and plugin metadata stay aligned.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "thumbgate",
|
|
3
3
|
"displayName": "ThumbGate",
|
|
4
4
|
"description": "👍👎 Thumbs down a mistake — your AI agent won't repeat it. Thumbs up good work — it remembers the pattern.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.1.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Igor Ganapolsky"
|
|
8
8
|
},
|
|
@@ -25,7 +25,7 @@ The portable profile adds this MCP server entry:
|
|
|
25
25
|
"mcp": {
|
|
26
26
|
"thumbgate": {
|
|
27
27
|
"type": "local",
|
|
28
|
-
"command": ["npx", "--yes", "--package", "thumbgate@1.
|
|
28
|
+
"command": ["npx", "--yes", "--package", "thumbgate@1.1.0", "thumbgate", "serve"],
|
|
29
29
|
"enabled": true
|
|
30
30
|
}
|
|
31
31
|
}
|
package/public/index.html
CHANGED
|
@@ -66,7 +66,9 @@ __GA_BOOTSTRAP__
|
|
|
66
66
|
"Background Agent Governance — per-agent pass rates, CI auto-feedback",
|
|
67
67
|
"Memory Migration — imports Claude Code MEMORY.md into unlimited SQLite DB",
|
|
68
68
|
"Prompt-Level DLP — scans tool call inputs before execution",
|
|
69
|
-
"Per-Step Scoring — every gate decision becomes a DPO/KTO training signal"
|
|
69
|
+
"Per-Step Scoring — every gate decision becomes a DPO/KTO training signal",
|
|
70
|
+
"HuggingFace Export — share PII-redacted agent traces as open training datasets",
|
|
71
|
+
"Unified Context — one-call context assembly with session, lessons, guards, and code-graph"
|
|
70
72
|
],
|
|
71
73
|
"offers": [
|
|
72
74
|
{
|
|
@@ -578,7 +580,7 @@ __GA_BOOTSTRAP__
|
|
|
578
580
|
<!-- HOW IT WORKS -->
|
|
579
581
|
<section class="how-it-works" id="how-it-works">
|
|
580
582
|
<div class="container">
|
|
581
|
-
<div class="section-label">New in v1.
|
|
583
|
+
<div class="section-label">New in v1.1.0</div>
|
|
582
584
|
<h2 class="section-title">Three steps to stop repeated AI failures</h2>
|
|
583
585
|
<div class="steps">
|
|
584
586
|
<div class="step">
|
|
@@ -670,6 +672,7 @@ __GA_BOOTSTRAP__
|
|
|
670
672
|
<li>All MCP integrations (Claude Code, Cursor, Codex, etc.)</li>
|
|
671
673
|
<li>PreToolUse hook blocking</li>
|
|
672
674
|
<li>Local SQLite lesson DB</li>
|
|
675
|
+
<li>Unified context assembly — one call gets session, lessons, guards, and code-graph</li>
|
|
673
676
|
<li><a href="/guide" style="color:var(--cyan);text-decoration:underline;">Setup guide for all agents →</a></li>
|
|
674
677
|
</ul>
|
|
675
678
|
<a href="https://www.npmjs.com/package/thumbgate" target="_blank" rel="noopener" class="btn-free">Install Free</a>
|
|
@@ -698,6 +701,7 @@ __GA_BOOTSTRAP__
|
|
|
698
701
|
<li><a href="/dashboard" style="color:var(--cyan);text-decoration:underline;">Visual gate debugger →</a> see every blocked action and the gate that fired so you can trust the system in minutes</li>
|
|
699
702
|
<li>Auto-connect — activate once with your license key, then your running agents appear automatically on your local dashboard</li>
|
|
700
703
|
<li><a href="/dashboard" style="color:var(--cyan);text-decoration:underline;">DPO training data export →</a> turn real thumbs-downs into ready-to-use preference pairs for fine-tuning (LoRA / JSONL)</li>
|
|
704
|
+
<li><strong>HuggingFace dataset export</strong> — share PII-redacted agent traces as open training datasets (<code>npm run export:hf</code>)</li>
|
|
701
705
|
<li><strong>Model Hardening Advisor</strong> — get recommendations on when and how to fine-tune your model to natively avoid recurring failures</li>
|
|
702
706
|
<li>Personal local dashboard — every Pro user gets a localhost dashboard without extra cloud setup</li>
|
|
703
707
|
<li>Founder-license support — we help you wire the riskiest flows first: migrations, force-pushes, deploys, and CI</li>
|
|
@@ -835,7 +839,7 @@ __GA_BOOTSTRAP__
|
|
|
835
839
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
836
840
|
<a href="/blog">Blog</a>
|
|
837
841
|
</div>
|
|
838
|
-
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.
|
|
842
|
+
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.1.0</span>
|
|
839
843
|
</div>
|
|
840
844
|
</footer>
|
|
841
845
|
|
|
Binary file
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HuggingFace Dataset Exporter
|
|
6
|
+
*
|
|
7
|
+
* Exports ThumbGate agent traces as a HuggingFace-compatible dataset in two formats:
|
|
8
|
+
*
|
|
9
|
+
* 1. Agent Traces (traces split) — raw feedback entries with tool calls, signals,
|
|
10
|
+
* context, and outcomes. Matches the "share your agent traces" initiative.
|
|
11
|
+
*
|
|
12
|
+
* 2. DPO Preferences (preferences split) — chosen/rejected preference pairs
|
|
13
|
+
* derived from error→learning memory promotion. Ready for DPO/RLHF training.
|
|
14
|
+
*
|
|
15
|
+
* Output: Parquet-compatible JSONL files + dataset_info.json (HF Dataset Card metadata).
|
|
16
|
+
*
|
|
17
|
+
* HuggingFace Datasets format:
|
|
18
|
+
* dataset_dir/
|
|
19
|
+
* dataset_info.json — metadata, features schema, splits
|
|
20
|
+
* traces.jsonl — agent trace rows
|
|
21
|
+
* preferences.jsonl — DPO preference pair rows
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
27
|
+
const { exportDpoFromMemories } = require('./export-dpo-pairs');
|
|
28
|
+
const { getProvenance } = require('./contextfs');
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
function readJSONL(filePath) {
|
|
35
|
+
if (!fs.existsSync(filePath)) return [];
|
|
36
|
+
const raw = fs.readFileSync(filePath, 'utf-8').trim();
|
|
37
|
+
if (!raw) return [];
|
|
38
|
+
return raw
|
|
39
|
+
.split('\n')
|
|
40
|
+
.map((line) => {
|
|
41
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
42
|
+
})
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ensureDir(dirPath) {
|
|
47
|
+
if (!fs.existsSync(dirPath)) {
|
|
48
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function writeJSONL(filePath, rows) {
|
|
53
|
+
const content = rows.map((row) => JSON.stringify(row)).join('\n');
|
|
54
|
+
fs.writeFileSync(filePath, content ? `${content}\n` : '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// PII / path redaction
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
function redactPaths(text) {
|
|
62
|
+
if (!text || typeof text !== 'string') return text || '';
|
|
63
|
+
return text
|
|
64
|
+
.replace(/\/Users\/[^\s/]+/g, '/Users/redacted')
|
|
65
|
+
.replace(/\/home\/[^\s/]+/g, '/home/redacted')
|
|
66
|
+
.replace(/C:\\Users\\[^\s\\]+/g, 'C:\\Users\\redacted');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function redactEntry(obj) {
|
|
70
|
+
if (!obj || typeof obj !== 'object') return obj;
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
73
|
+
if (typeof value === 'string') {
|
|
74
|
+
out[key] = redactPaths(value);
|
|
75
|
+
} else if (Array.isArray(value)) {
|
|
76
|
+
out[key] = value.map((v) => (typeof v === 'string' ? redactPaths(v) : v));
|
|
77
|
+
} else {
|
|
78
|
+
out[key] = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Trace row builder — converts feedback-log entries to HF trace rows
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
function buildTraceRow(entry, index) {
|
|
89
|
+
return {
|
|
90
|
+
trace_id: entry.id || `trace_${index}`,
|
|
91
|
+
timestamp: entry.timestamp || null,
|
|
92
|
+
signal: entry.signal || entry.feedback || 'unknown',
|
|
93
|
+
tool_name: entry.toolName || entry.actionType || 'unknown',
|
|
94
|
+
context: redactPaths(entry.context || ''),
|
|
95
|
+
what_worked: redactPaths(entry.whatWorked || ''),
|
|
96
|
+
what_went_wrong: redactPaths(entry.whatWentWrong || ''),
|
|
97
|
+
what_to_change: redactPaths(entry.whatToChange || ''),
|
|
98
|
+
tags: Array.isArray(entry.tags) ? entry.tags : [],
|
|
99
|
+
failure_type: entry.failureType || null,
|
|
100
|
+
source: 'thumbgate',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Preference row builder — converts DPO pairs to HF preference rows
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
function buildPreferenceRow(pair, index) {
|
|
109
|
+
return {
|
|
110
|
+
pair_id: `pref_${index}`,
|
|
111
|
+
prompt: redactPaths(pair.prompt || ''),
|
|
112
|
+
chosen: redactPaths(pair.chosen || ''),
|
|
113
|
+
rejected: redactPaths(pair.rejected || ''),
|
|
114
|
+
match_score: pair.metadata ? pair.metadata.matchScore : null,
|
|
115
|
+
matched_keys: pair.metadata ? pair.metadata.matchedKeys || [] : [],
|
|
116
|
+
rubric_delta: pair.metadata && pair.metadata.rubric
|
|
117
|
+
? pair.metadata.rubric.weightedDelta
|
|
118
|
+
: null,
|
|
119
|
+
source: 'thumbgate',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Dataset info (HuggingFace Dataset Card metadata)
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
function buildDatasetInfo({ traceCount, preferenceCount, exportedAt }) {
|
|
128
|
+
return {
|
|
129
|
+
dataset_info: {
|
|
130
|
+
description: 'Agent traces and DPO preference pairs from ThumbGate — pre-action gates for AI coding agents. Contains real-world tool call feedback, failure patterns, and learned corrections.',
|
|
131
|
+
citation: '',
|
|
132
|
+
homepage: 'https://github.com/IgorGanapolsky/ThumbGate',
|
|
133
|
+
license: 'MIT',
|
|
134
|
+
features: {
|
|
135
|
+
traces: {
|
|
136
|
+
trace_id: { dtype: 'string' },
|
|
137
|
+
timestamp: { dtype: 'string' },
|
|
138
|
+
signal: { dtype: 'string' },
|
|
139
|
+
tool_name: { dtype: 'string' },
|
|
140
|
+
context: { dtype: 'string' },
|
|
141
|
+
what_worked: { dtype: 'string' },
|
|
142
|
+
what_went_wrong: { dtype: 'string' },
|
|
143
|
+
what_to_change: { dtype: 'string' },
|
|
144
|
+
tags: { dtype: 'list', inner: { dtype: 'string' } },
|
|
145
|
+
failure_type: { dtype: 'string' },
|
|
146
|
+
source: { dtype: 'string' },
|
|
147
|
+
},
|
|
148
|
+
preferences: {
|
|
149
|
+
pair_id: { dtype: 'string' },
|
|
150
|
+
prompt: { dtype: 'string' },
|
|
151
|
+
chosen: { dtype: 'string' },
|
|
152
|
+
rejected: { dtype: 'string' },
|
|
153
|
+
match_score: { dtype: 'float32' },
|
|
154
|
+
matched_keys: { dtype: 'list', inner: { dtype: 'string' } },
|
|
155
|
+
rubric_delta: { dtype: 'float32' },
|
|
156
|
+
source: { dtype: 'string' },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
splits: {
|
|
160
|
+
traces: { num_examples: traceCount },
|
|
161
|
+
preferences: { num_examples: preferenceCount },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
exported_at: exportedAt,
|
|
165
|
+
exporter: 'thumbgate/export-hf-dataset',
|
|
166
|
+
version: '1.0.0',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Main export function
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Export ThumbGate data as a HuggingFace-compatible dataset.
|
|
176
|
+
*
|
|
177
|
+
* @param {Object} options
|
|
178
|
+
* @param {string} [options.outputDir] - Directory to write dataset files
|
|
179
|
+
* @param {string} [options.feedbackDir] - Override feedback data directory
|
|
180
|
+
* @param {boolean} [options.includeProvenance] - Include provenance events in traces
|
|
181
|
+
* @returns {Object} Export summary
|
|
182
|
+
*/
|
|
183
|
+
function exportHfDataset(options = {}) {
|
|
184
|
+
const feedbackDir = options.feedbackDir || resolveFeedbackDir();
|
|
185
|
+
const outputDir = options.outputDir || path.join(feedbackDir, 'hf-dataset');
|
|
186
|
+
const includeProvenance = options.includeProvenance !== false;
|
|
187
|
+
|
|
188
|
+
ensureDir(outputDir);
|
|
189
|
+
|
|
190
|
+
// --- Traces split ---
|
|
191
|
+
const feedbackLogPath = path.join(feedbackDir, 'feedback-log.jsonl');
|
|
192
|
+
const feedbackEntries = readJSONL(feedbackLogPath);
|
|
193
|
+
const traceRows = feedbackEntries.map((entry, i) => buildTraceRow(redactEntry(entry), i));
|
|
194
|
+
|
|
195
|
+
// Optionally append provenance events as traces
|
|
196
|
+
if (includeProvenance) {
|
|
197
|
+
try {
|
|
198
|
+
const provenanceEvents = getProvenance(200);
|
|
199
|
+
for (const evt of provenanceEvents) {
|
|
200
|
+
traceRows.push({
|
|
201
|
+
trace_id: evt.id || `prov_${traceRows.length}`,
|
|
202
|
+
timestamp: evt.timestamp || null,
|
|
203
|
+
signal: 'provenance',
|
|
204
|
+
tool_name: evt.type || 'context_assembly',
|
|
205
|
+
context: redactPaths(JSON.stringify(evt).slice(0, 500)),
|
|
206
|
+
what_worked: '',
|
|
207
|
+
what_went_wrong: '',
|
|
208
|
+
what_to_change: '',
|
|
209
|
+
tags: ['provenance'],
|
|
210
|
+
failure_type: null,
|
|
211
|
+
source: 'thumbgate',
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
} catch {
|
|
215
|
+
// Provenance read failure should not break export
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
writeJSONL(path.join(outputDir, 'traces.jsonl'), traceRows);
|
|
220
|
+
|
|
221
|
+
// --- Preferences split ---
|
|
222
|
+
const memoryLogPath = path.join(feedbackDir, 'memory-log.jsonl');
|
|
223
|
+
const memories = readJSONL(memoryLogPath);
|
|
224
|
+
let preferenceRows = [];
|
|
225
|
+
|
|
226
|
+
if (memories.length > 0) {
|
|
227
|
+
try {
|
|
228
|
+
const dpoResult = exportDpoFromMemories(memories);
|
|
229
|
+
preferenceRows = dpoResult.pairs.map((pair, i) => buildPreferenceRow(pair, i));
|
|
230
|
+
} catch {
|
|
231
|
+
// DPO export failure should not break the traces export
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
writeJSONL(path.join(outputDir, 'preferences.jsonl'), preferenceRows);
|
|
236
|
+
|
|
237
|
+
// --- Dataset info ---
|
|
238
|
+
const exportedAt = new Date().toISOString();
|
|
239
|
+
const info = buildDatasetInfo({
|
|
240
|
+
traceCount: traceRows.length,
|
|
241
|
+
preferenceCount: preferenceRows.length,
|
|
242
|
+
exportedAt,
|
|
243
|
+
});
|
|
244
|
+
fs.writeFileSync(
|
|
245
|
+
path.join(outputDir, 'dataset_info.json'),
|
|
246
|
+
JSON.stringify(info, null, 2) + '\n',
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
outputDir,
|
|
251
|
+
traceCount: traceRows.length,
|
|
252
|
+
preferenceCount: preferenceRows.length,
|
|
253
|
+
files: ['traces.jsonl', 'preferences.jsonl', 'dataset_info.json'],
|
|
254
|
+
exportedAt,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// CLI
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
function main() {
|
|
263
|
+
const args = {};
|
|
264
|
+
process.argv.slice(2).forEach((arg) => {
|
|
265
|
+
if (!arg.startsWith('--')) return;
|
|
266
|
+
const [key, ...rest] = arg.slice(2).split('=');
|
|
267
|
+
args[key] = rest.length ? rest.join('=') : true;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const result = exportHfDataset({
|
|
271
|
+
outputDir: args.output || undefined,
|
|
272
|
+
includeProvenance: args.provenance !== 'false',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
console.log(`Exported HuggingFace dataset to ${result.outputDir}`);
|
|
276
|
+
console.log(` Traces: ${result.traceCount}`);
|
|
277
|
+
console.log(` Preferences: ${result.preferenceCount}`);
|
|
278
|
+
console.log(` Files: ${result.files.join(', ')}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (require.main === module) {
|
|
282
|
+
main();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
module.exports = {
|
|
286
|
+
exportHfDataset,
|
|
287
|
+
buildTraceRow,
|
|
288
|
+
buildPreferenceRow,
|
|
289
|
+
buildDatasetInfo,
|
|
290
|
+
redactPaths,
|
|
291
|
+
redactEntry,
|
|
292
|
+
readJSONL,
|
|
293
|
+
};
|
package/scripts/tool-registry.js
CHANGED
|
@@ -399,6 +399,17 @@ const TOOLS = [
|
|
|
399
399
|
},
|
|
400
400
|
},
|
|
401
401
|
}),
|
|
402
|
+
destructiveTool({
|
|
403
|
+
name: 'export_hf_dataset',
|
|
404
|
+
description: 'Export ThumbGate agent traces and DPO preference pairs as a HuggingFace-compatible dataset. Produces traces.jsonl, preferences.jsonl, and dataset_info.json with PII-redacted paths. Ready for huggingface-cli upload.',
|
|
405
|
+
inputSchema: {
|
|
406
|
+
type: 'object',
|
|
407
|
+
properties: {
|
|
408
|
+
outputDir: { type: 'string', description: 'Output directory (default: feedback-dir/hf-dataset)' },
|
|
409
|
+
includeProvenance: { type: 'boolean', description: 'Include provenance events in traces (default: true)' },
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
}),
|
|
402
413
|
destructiveTool({
|
|
403
414
|
name: 'export_databricks_bundle',
|
|
404
415
|
description: 'Export ThumbGate logs and proof artifacts as a Databricks-ready analytics bundle',
|