thumbgate 0.9.10

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.
Files changed (364) hide show
  1. package/.claude-plugin/README.md +134 -0
  2. package/.claude-plugin/bundle/icon.png +0 -0
  3. package/.claude-plugin/bundle/icon.svg +18 -0
  4. package/.claude-plugin/bundle/server/index.js +24 -0
  5. package/.claude-plugin/marketplace.json +36 -0
  6. package/.claude-plugin/plugin.json +21 -0
  7. package/.well-known/mcp/server-card.json +231 -0
  8. package/LICENSE +21 -0
  9. package/README.md +375 -0
  10. package/adapters/README.md +9 -0
  11. package/adapters/amp/skills/thumbgate-feedback/SKILL.md +22 -0
  12. package/adapters/chatgpt/INSTALL.md +83 -0
  13. package/adapters/chatgpt/openapi.yaml +1281 -0
  14. package/adapters/claude/.mcp.json +14 -0
  15. package/adapters/codex/config.toml +9 -0
  16. package/adapters/gemini/function-declarations.json +224 -0
  17. package/adapters/mcp/server-stdio.js +788 -0
  18. package/adapters/opencode/opencode.json +15 -0
  19. package/bin/cli.js +1484 -0
  20. package/bin/memory.sh +64 -0
  21. package/bin/obsidian-sync.sh +20 -0
  22. package/bin/postinstall.js +37 -0
  23. package/config/build-metadata.json +4 -0
  24. package/config/e2e-critical-flows.json +45 -0
  25. package/config/gate-templates.json +77 -0
  26. package/config/gates/claim-verification.json +29 -0
  27. package/config/gates/computer-use.json +39 -0
  28. package/config/gates/default.json +117 -0
  29. package/config/github-about.json +25 -0
  30. package/config/mcp-allowlists.json +135 -0
  31. package/config/model-tiers.json +33 -0
  32. package/config/partner-routing.json +132 -0
  33. package/config/policy-bundles/constrained-v1.json +64 -0
  34. package/config/policy-bundles/default-v1.json +91 -0
  35. package/config/rubrics/default-v1.json +52 -0
  36. package/config/skill-packs/react-testing.json +23 -0
  37. package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
  38. package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
  39. package/config/skill-specs/pr-reviewer.json +9 -0
  40. package/config/skill-specs/release-status.json +9 -0
  41. package/config/skill-specs/ticket-triage.json +9 -0
  42. package/config/subagent-profiles.json +32 -0
  43. package/config/tessl-tiles.json +29 -0
  44. package/config/thumbgate-settings.managed.json +12 -0
  45. package/openapi/openapi.yaml +1281 -0
  46. package/package.json +283 -0
  47. package/plugins/amp-skill/INSTALL.md +52 -0
  48. package/plugins/amp-skill/SKILL.md +64 -0
  49. package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
  50. package/plugins/claude-codex-bridge/.mcp.json +12 -0
  51. package/plugins/claude-codex-bridge/INSTALL.md +43 -0
  52. package/plugins/claude-codex-bridge/README.md +46 -0
  53. package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
  54. package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
  55. package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
  56. package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
  57. package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
  58. package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
  59. package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
  60. package/plugins/claude-skill/INSTALL.md +55 -0
  61. package/plugins/claude-skill/SKILL.md +46 -0
  62. package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
  63. package/plugins/codex-profile/.mcp.json +12 -0
  64. package/plugins/codex-profile/AGENTS.md +20 -0
  65. package/plugins/codex-profile/INSTALL.md +66 -0
  66. package/plugins/codex-profile/README.md +37 -0
  67. package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
  68. package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
  69. package/plugins/cursor-marketplace/LICENSE +21 -0
  70. package/plugins/cursor-marketplace/README.md +124 -0
  71. package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
  72. package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
  73. package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
  74. package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
  75. package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
  76. package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
  77. package/plugins/cursor-marketplace/mcp.json +12 -0
  78. package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
  79. package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
  80. package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
  81. package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
  82. package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
  83. package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
  84. package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
  85. package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
  86. package/plugins/gemini-extension/INSTALL.md +92 -0
  87. package/plugins/gemini-extension/gemini_prompt.txt +14 -0
  88. package/plugins/gemini-extension/tool_contract.json +45 -0
  89. package/plugins/opencode-profile/INSTALL.md +57 -0
  90. package/public/assets/instagram-card.png +0 -0
  91. package/public/assets/tiktok-agent-memory.mp4 +0 -0
  92. package/public/blog.html +400 -0
  93. package/public/dashboard.html +1093 -0
  94. package/public/guide.html +317 -0
  95. package/public/index.html +1014 -0
  96. package/public/learn/agent-harness-pattern.html +180 -0
  97. package/public/learn/ai-agent-persistent-memory.html +202 -0
  98. package/public/learn/learn.css +45 -0
  99. package/public/learn/mcp-pre-action-gates-explained.html +172 -0
  100. package/public/learn/stop-ai-agent-force-push.html +134 -0
  101. package/public/learn/vibe-coding-safety-net.html +142 -0
  102. package/public/learn.html +213 -0
  103. package/public/lessons.html +650 -0
  104. package/public/vercel.json +8 -0
  105. package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
  106. package/scripts/a2ui-engine.js +73 -0
  107. package/scripts/access-anomaly-detector.js +12 -0
  108. package/scripts/adk-consolidator.js +266 -0
  109. package/scripts/agent-readiness.js +220 -0
  110. package/scripts/agent-security-hardening.js +227 -0
  111. package/scripts/agentic-data-pipeline.js +847 -0
  112. package/scripts/analytics-report.js +328 -0
  113. package/scripts/analytics-window.js +158 -0
  114. package/scripts/async-job-runner.js +1001 -0
  115. package/scripts/audit-trail.js +398 -0
  116. package/scripts/auto-promote-gates.js +299 -0
  117. package/scripts/auto-wire-hooks.js +312 -0
  118. package/scripts/autonomous-sales-agent.js +39 -0
  119. package/scripts/autoresearch-runner.js +216 -0
  120. package/scripts/background-agent-governance.js +237 -0
  121. package/scripts/behavioral-extraction.js +97 -0
  122. package/scripts/belief-update.js +84 -0
  123. package/scripts/billing.js +2438 -0
  124. package/scripts/bot-detector.js +50 -0
  125. package/scripts/budget-guard.js +173 -0
  126. package/scripts/build-claude-mcpb.js +189 -0
  127. package/scripts/build-metadata.js +97 -0
  128. package/scripts/check-congruence.js +322 -0
  129. package/scripts/cli-feedback.js +135 -0
  130. package/scripts/cli-telemetry.js +87 -0
  131. package/scripts/cloudflare-dynamic-sandbox.js +315 -0
  132. package/scripts/code-reasoning.js +350 -0
  133. package/scripts/codegraph-context.js +466 -0
  134. package/scripts/commercial-offer.js +56 -0
  135. package/scripts/computer-use-firewall.js +250 -0
  136. package/scripts/context-engine.js +694 -0
  137. package/scripts/contextfs.js +1287 -0
  138. package/scripts/conversation-context.js +119 -0
  139. package/scripts/creator-campaigns.js +239 -0
  140. package/scripts/daemon-manager.js +108 -0
  141. package/scripts/daily-digest.js +11 -0
  142. package/scripts/dashboard-render-spec.js +395 -0
  143. package/scripts/dashboard.js +1058 -0
  144. package/scripts/data-governance.js +173 -0
  145. package/scripts/delegation-runtime.js +900 -0
  146. package/scripts/deploy-gcp.sh +44 -0
  147. package/scripts/deploy-policy.js +263 -0
  148. package/scripts/disagreement-mining.js +315 -0
  149. package/scripts/dispatch-brief.js +159 -0
  150. package/scripts/distribution-surfaces.js +44 -0
  151. package/scripts/dpo-optimizer.js +209 -0
  152. package/scripts/ephemeral-agent-store.js +219 -0
  153. package/scripts/eval-harness.js +56 -0
  154. package/scripts/evolution-state.js +241 -0
  155. package/scripts/experiment-tracker.js +267 -0
  156. package/scripts/export-databricks-bundle.js +242 -0
  157. package/scripts/export-dpo-pairs.js +345 -0
  158. package/scripts/export-kto-pairs.js +310 -0
  159. package/scripts/export-training.js +448 -0
  160. package/scripts/failure-diagnostics.js +558 -0
  161. package/scripts/feedback-attribution.js +313 -0
  162. package/scripts/feedback-fallback.js +111 -0
  163. package/scripts/feedback-history-distiller.js +391 -0
  164. package/scripts/feedback-inbox-read.js +162 -0
  165. package/scripts/feedback-loop.js +1887 -0
  166. package/scripts/feedback-paths.js +145 -0
  167. package/scripts/feedback-quality.js +139 -0
  168. package/scripts/feedback-root-consolidator.js +238 -0
  169. package/scripts/feedback-schema.js +426 -0
  170. package/scripts/feedback-session.js +286 -0
  171. package/scripts/feedback-to-memory.js +185 -0
  172. package/scripts/feedback-to-rules.js +163 -0
  173. package/scripts/filesystem-search.js +404 -0
  174. package/scripts/funnel-analytics.js +35 -0
  175. package/scripts/gate-satisfy.js +42 -0
  176. package/scripts/gate-stats.js +116 -0
  177. package/scripts/gate-templates.js +70 -0
  178. package/scripts/gates-engine.js +816 -0
  179. package/scripts/generate-paperbanana-diagrams.sh +99 -0
  180. package/scripts/generate-pretool-hook.sh +40 -0
  181. package/scripts/github-about.js +350 -0
  182. package/scripts/github-outreach.js +65 -0
  183. package/scripts/gtm-revenue-loop.js +520 -0
  184. package/scripts/hallucination-detector.js +226 -0
  185. package/scripts/hf-papers.js +317 -0
  186. package/scripts/history-distiller.js +200 -0
  187. package/scripts/hook-auto-capture.sh +95 -0
  188. package/scripts/hook-stop-pr-thread-check.sh +68 -0
  189. package/scripts/hook-stop-self-score.sh +51 -0
  190. package/scripts/hook-stop-verify-deploy.sh +31 -0
  191. package/scripts/hook-thumbgate-cache-updater.js +48 -0
  192. package/scripts/hook-verify-before-done.sh +20 -0
  193. package/scripts/hosted-config.js +170 -0
  194. package/scripts/hybrid-feedback-context.js +676 -0
  195. package/scripts/install-mcp.js +159 -0
  196. package/scripts/intent-router.js +392 -0
  197. package/scripts/internal-agent-bootstrap.js +490 -0
  198. package/scripts/jsonl-watcher.js +155 -0
  199. package/scripts/lesson-db.js +613 -0
  200. package/scripts/lesson-inference.js +315 -0
  201. package/scripts/lesson-retrieval.js +95 -0
  202. package/scripts/lesson-rotation.js +137 -0
  203. package/scripts/lesson-search.js +644 -0
  204. package/scripts/lesson-synthesis.js +196 -0
  205. package/scripts/license.js +50 -0
  206. package/scripts/local-model-profile.js +383 -0
  207. package/scripts/markdown-escape.js +12 -0
  208. package/scripts/marketing-experiment.js +671 -0
  209. package/scripts/mcp-config.js +149 -0
  210. package/scripts/mcp-policy.js +99 -0
  211. package/scripts/memalign-recall.js +111 -0
  212. package/scripts/memory-firewall.js +222 -0
  213. package/scripts/memory-migration.js +296 -0
  214. package/scripts/meta-policy.js +194 -0
  215. package/scripts/metered-billing.js +16 -0
  216. package/scripts/model-tier-router.js +301 -0
  217. package/scripts/money-watcher.js +71 -0
  218. package/scripts/multi-hop-recall.js +240 -0
  219. package/scripts/natural-language-harness.js +330 -0
  220. package/scripts/obsidian-export.js +712 -0
  221. package/scripts/operational-dashboard.js +103 -0
  222. package/scripts/operational-summary.js +93 -0
  223. package/scripts/optimize-context.js +17 -0
  224. package/scripts/org-dashboard.js +201 -0
  225. package/scripts/partner-orchestration.js +146 -0
  226. package/scripts/per-step-scoring.js +165 -0
  227. package/scripts/perplexity-marketing.js +466 -0
  228. package/scripts/pii-scanner.js +153 -0
  229. package/scripts/plan-gate.js +154 -0
  230. package/scripts/post-everywhere.js +308 -0
  231. package/scripts/post-to-x-retry.sh +22 -0
  232. package/scripts/post-to-x.js +369 -0
  233. package/scripts/pr-manager.js +236 -0
  234. package/scripts/predictive-insights.js +356 -0
  235. package/scripts/principle-extractor.js +162 -0
  236. package/scripts/pro-features.js +40 -0
  237. package/scripts/pro-local-dashboard.js +174 -0
  238. package/scripts/problem-detail.js +53 -0
  239. package/scripts/product-feedback.js +134 -0
  240. package/scripts/profile-router.js +245 -0
  241. package/scripts/prompt-dlp.js +221 -0
  242. package/scripts/prompt-guard.js +83 -0
  243. package/scripts/prove-adapters.js +863 -0
  244. package/scripts/prove-attribution.js +365 -0
  245. package/scripts/prove-automation.js +653 -0
  246. package/scripts/prove-autoresearch.js +304 -0
  247. package/scripts/prove-claim-verification.js +277 -0
  248. package/scripts/prove-cloudflare-sandbox.js +163 -0
  249. package/scripts/prove-data-pipeline.js +410 -0
  250. package/scripts/prove-data-quality.js +227 -0
  251. package/scripts/prove-evolution.js +352 -0
  252. package/scripts/prove-harnesses.js +287 -0
  253. package/scripts/prove-intelligence.js +259 -0
  254. package/scripts/prove-lancedb.js +371 -0
  255. package/scripts/prove-local-intelligence.js +342 -0
  256. package/scripts/prove-loop-closure.js +263 -0
  257. package/scripts/prove-predictive-insights.js +357 -0
  258. package/scripts/prove-runtime.js +350 -0
  259. package/scripts/prove-seo-gsd.js +234 -0
  260. package/scripts/prove-settings.js +279 -0
  261. package/scripts/prove-subway-upgrades.js +277 -0
  262. package/scripts/prove-tessl.js +229 -0
  263. package/scripts/prove-training-export.js +327 -0
  264. package/scripts/prove-workflow-contract.js +116 -0
  265. package/scripts/prove-xmemory.js +332 -0
  266. package/scripts/publish-decision.js +133 -0
  267. package/scripts/pulse.js +80 -0
  268. package/scripts/rate-limiter.js +125 -0
  269. package/scripts/reddit-dm-outreach.js +182 -0
  270. package/scripts/reddit-monitor-cron.sh +26 -0
  271. package/scripts/reflector-agent.js +221 -0
  272. package/scripts/reminder-engine.js +132 -0
  273. package/scripts/revenue-status.js +472 -0
  274. package/scripts/risk-scorer.js +458 -0
  275. package/scripts/rlaif-self-audit.js +129 -0
  276. package/scripts/rubric-engine.js +230 -0
  277. package/scripts/schedule-manager.js +251 -0
  278. package/scripts/secret-scanner.js +414 -0
  279. package/scripts/self-heal.js +147 -0
  280. package/scripts/self-healing-check.js +188 -0
  281. package/scripts/semantic-layer.js +98 -0
  282. package/scripts/seo-gsd.js +1153 -0
  283. package/scripts/settings-hierarchy.js +214 -0
  284. package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
  285. package/scripts/skill-exporter.js +262 -0
  286. package/scripts/skill-generator.js +446 -0
  287. package/scripts/skill-materializer.js +134 -0
  288. package/scripts/skill-packs.js +136 -0
  289. package/scripts/skill-proposer.js +99 -0
  290. package/scripts/skill-quality-tracker.js +284 -0
  291. package/scripts/slo-alert-engine.js +14 -0
  292. package/scripts/slow-loop.js +72 -0
  293. package/scripts/social-analytics/db/schema.sql +32 -0
  294. package/scripts/social-analytics/digest.js +256 -0
  295. package/scripts/social-analytics/generate-instagram-card.js +97 -0
  296. package/scripts/social-analytics/instagram-thumbgate-post.js +73 -0
  297. package/scripts/social-analytics/mcp-server.js +289 -0
  298. package/scripts/social-analytics/normalizer.js +580 -0
  299. package/scripts/social-analytics/notify.js +162 -0
  300. package/scripts/social-analytics/poll-all.js +107 -0
  301. package/scripts/social-analytics/pollers/github.js +195 -0
  302. package/scripts/social-analytics/pollers/instagram.js +253 -0
  303. package/scripts/social-analytics/pollers/linkedin.js +330 -0
  304. package/scripts/social-analytics/pollers/plausible.js +247 -0
  305. package/scripts/social-analytics/pollers/reddit.js +306 -0
  306. package/scripts/social-analytics/pollers/threads.js +233 -0
  307. package/scripts/social-analytics/pollers/tiktok.js +203 -0
  308. package/scripts/social-analytics/pollers/x.js +227 -0
  309. package/scripts/social-analytics/pollers/youtube.js +304 -0
  310. package/scripts/social-analytics/pollers/zernio.js +180 -0
  311. package/scripts/social-analytics/publish-instagram-thumbgate.js +85 -0
  312. package/scripts/social-analytics/publishers/devto.js +122 -0
  313. package/scripts/social-analytics/publishers/instagram.js +317 -0
  314. package/scripts/social-analytics/publishers/linkedin.js +294 -0
  315. package/scripts/social-analytics/publishers/reddit.js +390 -0
  316. package/scripts/social-analytics/publishers/threads.js +275 -0
  317. package/scripts/social-analytics/publishers/tiktok.js +217 -0
  318. package/scripts/social-analytics/publishers/x.js +259 -0
  319. package/scripts/social-analytics/publishers/youtube.js +223 -0
  320. package/scripts/social-analytics/publishers/zernio.js +209 -0
  321. package/scripts/social-analytics/run-digest.js +34 -0
  322. package/scripts/social-analytics/store.js +257 -0
  323. package/scripts/social-analytics/utm.js +143 -0
  324. package/scripts/social-pipeline.js +2628 -0
  325. package/scripts/social-quality-gate.js +18 -0
  326. package/scripts/social-reply-monitor.js +445 -0
  327. package/scripts/status-dashboard.js +155 -0
  328. package/scripts/statusline-lesson.js +16 -0
  329. package/scripts/statusline-tower.js +8 -0
  330. package/scripts/statusline.sh +116 -0
  331. package/scripts/stripe-live-status.js +115 -0
  332. package/scripts/subagent-profiles.js +79 -0
  333. package/scripts/sync-gh-secrets-from-env.sh +70 -0
  334. package/scripts/sync-github-about.js +52 -0
  335. package/scripts/sync-version.js +451 -0
  336. package/scripts/synthetic-dpo.js +234 -0
  337. package/scripts/telemetry-analytics.js +821 -0
  338. package/scripts/tessl-export.js +371 -0
  339. package/scripts/test-coverage.js +120 -0
  340. package/scripts/thompson-sampling.js +417 -0
  341. package/scripts/thumbgate-search.js +189 -0
  342. package/scripts/tool-kpi-tracker.js +12 -0
  343. package/scripts/tool-registry.js +811 -0
  344. package/scripts/train_from_feedback.py +910 -0
  345. package/scripts/user-profile.js +78 -0
  346. package/scripts/validate-feedback.js +580 -0
  347. package/scripts/validate-workflow-contract.js +287 -0
  348. package/scripts/vector-store.js +198 -0
  349. package/scripts/verification-loop.js +291 -0
  350. package/scripts/verify-obsidian-setup.sh +269 -0
  351. package/scripts/verify-run.js +269 -0
  352. package/scripts/webhook-delivery.js +62 -0
  353. package/scripts/weekly-auto-post.js +124 -0
  354. package/scripts/workflow-runs.js +154 -0
  355. package/scripts/workflow-sprint-intake.js +475 -0
  356. package/scripts/workspace-evolver.js +374 -0
  357. package/scripts/x-autonomous-marketing.js +139 -0
  358. package/scripts/xmemory-lite.js +405 -0
  359. package/skills/agent-memory/SKILL.md +97 -0
  360. package/skills/solve-architecture-autonomy/SKILL.md +17 -0
  361. package/skills/solve-architecture-autonomy/tool.js +33 -0
  362. package/skills/thumbgate/SKILL.md +114 -0
  363. package/skills/thumbgate-feedback/SKILL.md +49 -0
  364. package/src/api/server.js +4208 -0
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const { getFeedbackPaths } = require('./feedback-loop');
8
+
9
+ const PROJECT_ROOT = path.join(__dirname, '..');
10
+ const DEFAULT_PROOF_DIR = process.env.THUMBGATE_PROOF_DIR
11
+ || path.join(PROJECT_ROOT, 'proof');
12
+
13
+ function parseArgs(argv) {
14
+ const args = {};
15
+ argv.forEach((arg) => {
16
+ if (!arg.startsWith('--')) return;
17
+ const [key, ...rest] = arg.slice(2).split('=');
18
+ args[key] = rest.length ? rest.join('=') : true;
19
+ });
20
+ return args;
21
+ }
22
+
23
+ function ensureDir(dirPath) {
24
+ if (!fs.existsSync(dirPath)) {
25
+ fs.mkdirSync(dirPath, { recursive: true });
26
+ }
27
+ }
28
+
29
+ function readJSONL(filePath) {
30
+ if (!fs.existsSync(filePath)) return [];
31
+ const raw = fs.readFileSync(filePath, 'utf8').trim();
32
+ if (!raw) return [];
33
+ return raw
34
+ .split('\n')
35
+ .map((line) => {
36
+ try {
37
+ return JSON.parse(line);
38
+ } catch {
39
+ return null;
40
+ }
41
+ })
42
+ .filter(Boolean);
43
+ }
44
+
45
+ function readJSON(filePath) {
46
+ try {
47
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function writeJSONL(filePath, rows) {
54
+ const content = rows.map((row) => JSON.stringify(row)).join('\n');
55
+ fs.writeFileSync(filePath, content ? `${content}\n` : '');
56
+ }
57
+
58
+ function getDefaultFeedbackDir() {
59
+ return getFeedbackPaths().FEEDBACK_DIR;
60
+ }
61
+
62
+ function toBundleRelativePath(...segments) {
63
+ return path.posix.join(...segments);
64
+ }
65
+
66
+ function normalizeBundleRelativePath(relativePath) {
67
+ return String(relativePath || '').replace(/\\/g, '/');
68
+ }
69
+
70
+ function walkJsonFiles(dirPath, acc = []) {
71
+ if (!fs.existsSync(dirPath)) return acc;
72
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
73
+ for (const entry of entries) {
74
+ const fullPath = path.join(dirPath, entry.name);
75
+ if (entry.isDirectory()) {
76
+ walkJsonFiles(fullPath, acc);
77
+ } else if (entry.isFile() && entry.name.endsWith('.json')) {
78
+ acc.push(fullPath);
79
+ }
80
+ }
81
+ return acc;
82
+ }
83
+
84
+ function annotateRows(rows, dataset, sourceFile, exportedAt) {
85
+ return rows.map((row, index) => ({
86
+ bundleDataset: dataset,
87
+ bundleRowNumber: index + 1,
88
+ bundleExportedAt: exportedAt,
89
+ bundleSourceFile: sourceFile,
90
+ ...row,
91
+ }));
92
+ }
93
+
94
+ function collectProofReports(proofDir, exportedAt) {
95
+ return walkJsonFiles(proofDir)
96
+ .map((filePath, index) => ({
97
+ bundleDataset: 'proof_reports',
98
+ bundleRowNumber: index + 1,
99
+ bundleExportedAt: exportedAt,
100
+ reportId: path.basename(filePath, '.json'),
101
+ reportCategory: path.basename(path.dirname(filePath)),
102
+ reportPath: normalizeBundleRelativePath(path.relative(proofDir, filePath)),
103
+ report: readJSON(filePath),
104
+ }))
105
+ .filter((row) => row.report);
106
+ }
107
+
108
+ function buildSqlTemplate(manifest) {
109
+ const lines = [
110
+ '-- Databricks bootstrap for the exported analytics bundle.',
111
+ '-- Replace __CATALOG__, __SCHEMA__, and __BUNDLE_ROOT__ before running.',
112
+ '',
113
+ 'CREATE SCHEMA IF NOT EXISTS __CATALOG__.__SCHEMA__;',
114
+ '',
115
+ ];
116
+
117
+ for (const table of manifest.tables) {
118
+ lines.push(`CREATE OR REPLACE TABLE __CATALOG__.__SCHEMA__.${table.tableName} AS`);
119
+ lines.push('SELECT *, _metadata.file_path AS source_file');
120
+ lines.push(`FROM read_files('__BUNDLE_ROOT__/${normalizeBundleRelativePath(table.relativePath)}', format => 'json');`);
121
+ lines.push('');
122
+ }
123
+
124
+ return lines.join('\n');
125
+ }
126
+
127
+ function timestampSlug() {
128
+ return new Date().toISOString().replace(/[:.]/g, '-');
129
+ }
130
+
131
+ function exportDatabricksBundle(feedbackDir = getDefaultFeedbackDir(), outputPath, options = {}) {
132
+ const resolvedFeedbackDir = path.resolve(feedbackDir || getDefaultFeedbackDir());
133
+ const resolvedProofDir = path.resolve(options.proofDir || DEFAULT_PROOF_DIR);
134
+ const exportedAt = new Date().toISOString();
135
+ const bundlePath = path.resolve(outputPath || path.join(
136
+ resolvedFeedbackDir,
137
+ 'analytics',
138
+ `databricks-${timestampSlug()}`
139
+ ));
140
+ const tablesDir = path.join(bundlePath, 'tables');
141
+ ensureDir(tablesDir);
142
+
143
+ const datasets = [
144
+ {
145
+ tableName: 'feedback_events',
146
+ sourcePath: path.join(resolvedFeedbackDir, 'feedback-log.jsonl'),
147
+ description: 'Raw ThumbGate feedback events from feedback-log.jsonl',
148
+ },
149
+ {
150
+ tableName: 'memory_records',
151
+ sourcePath: path.join(resolvedFeedbackDir, 'memory-log.jsonl'),
152
+ description: 'Promoted learning and mistake memories from memory-log.jsonl',
153
+ },
154
+ {
155
+ tableName: 'feedback_sequences',
156
+ sourcePath: path.join(resolvedFeedbackDir, 'feedback-sequences.jsonl'),
157
+ description: 'Sequence-model training rows derived from accepted feedback',
158
+ },
159
+ {
160
+ tableName: 'feedback_attributions',
161
+ sourcePath: path.join(resolvedFeedbackDir, 'attributed-feedback.jsonl'),
162
+ description: 'Tool-call attribution rows for negative feedback events',
163
+ },
164
+ ];
165
+
166
+ const tables = datasets.map((dataset) => {
167
+ const rows = annotateRows(
168
+ readJSONL(dataset.sourcePath),
169
+ dataset.tableName,
170
+ path.basename(dataset.sourcePath),
171
+ exportedAt,
172
+ );
173
+ const fileName = `${dataset.tableName}.jsonl`;
174
+ const relativePath = toBundleRelativePath('tables', fileName);
175
+ writeJSONL(path.join(tablesDir, fileName), rows);
176
+ return {
177
+ tableName: dataset.tableName,
178
+ relativePath,
179
+ rowCount: rows.length,
180
+ description: dataset.description,
181
+ };
182
+ });
183
+
184
+ const proofRows = collectProofReports(resolvedProofDir, exportedAt);
185
+ const proofRelativePath = toBundleRelativePath('tables', 'proof_reports.jsonl');
186
+ writeJSONL(path.join(tablesDir, 'proof_reports.jsonl'), proofRows);
187
+ tables.push({
188
+ tableName: 'proof_reports',
189
+ relativePath: proofRelativePath,
190
+ rowCount: proofRows.length,
191
+ description: 'Machine-readable proof artifacts discovered under proof/**/*.json',
192
+ });
193
+
194
+ const manifest = {
195
+ format: 'databricks-analytics-bundle',
196
+ version: 1,
197
+ exportedAt,
198
+ bundlePath,
199
+ feedbackDir: resolvedFeedbackDir,
200
+ proofDir: resolvedProofDir,
201
+ placeholders: {
202
+ catalog: '__CATALOG__',
203
+ schema: '__SCHEMA__',
204
+ bundleRoot: '__BUNDLE_ROOT__',
205
+ },
206
+ tables,
207
+ };
208
+
209
+ const manifestPath = path.join(bundlePath, 'manifest.json');
210
+ const sqlTemplatePath = path.join(bundlePath, 'load_databricks.sql');
211
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
212
+ fs.writeFileSync(sqlTemplatePath, buildSqlTemplate(manifest) + '\n');
213
+
214
+ return {
215
+ bundlePath,
216
+ manifestPath,
217
+ sqlTemplatePath,
218
+ tableCount: tables.length,
219
+ totalRows: tables.reduce((sum, table) => sum + table.rowCount, 0),
220
+ tables,
221
+ };
222
+ }
223
+
224
+ module.exports = {
225
+ DEFAULT_PROOF_DIR,
226
+ buildSqlTemplate,
227
+ collectProofReports,
228
+ exportDatabricksBundle,
229
+ getDefaultFeedbackDir,
230
+ readJSONL,
231
+ toBundleRelativePath,
232
+ };
233
+
234
+ if (require.main === module) {
235
+ const args = parseArgs(process.argv.slice(2));
236
+ const result = exportDatabricksBundle(
237
+ args['feedback-dir'],
238
+ args.output,
239
+ { proofDir: args['proof-dir'] }
240
+ );
241
+ console.log(JSON.stringify(result, null, 2));
242
+ }
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * DPO Preference Pair Exporter
4
+ *
5
+ * Transforms error + learning memories into DPO JSONL triples.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { traceForDpoPair, aggregateTraces } = require('./code-reasoning');
11
+
12
+ const PROJECT_ROOT = path.join(__dirname, '..');
13
+ const FEEDBACK_DIR = process.env.THUMBGATE_FEEDBACK_DIR || path.join(PROJECT_ROOT, '.claude', 'memory', 'feedback');
14
+ const DEFAULT_LOCAL_MEMORY_LOG = path.join(FEEDBACK_DIR, 'memory-log.jsonl');
15
+
16
+ function readJSONL(filePath) {
17
+ if (!fs.existsSync(filePath)) return [];
18
+ const raw = fs.readFileSync(filePath, 'utf-8').trim();
19
+ if (!raw) return [];
20
+ return raw
21
+ .split('\n')
22
+ .map((line) => {
23
+ try {
24
+ return JSON.parse(line);
25
+ } catch {
26
+ return null;
27
+ }
28
+ })
29
+ .filter(Boolean);
30
+ }
31
+
32
+ function extractDomainKeys(memory) {
33
+ const keys = new Set();
34
+ const genericTags = new Set(['feedback', 'positive', 'negative']);
35
+
36
+ if (Array.isArray(memory.tags)) {
37
+ for (const tag of memory.tags) {
38
+ if (!genericTags.has(tag)) keys.add(tag);
39
+ }
40
+ }
41
+
42
+ const titleWords = (memory.title || '')
43
+ .replace(/^(MISTAKE|SUCCESS|ERROR|LEARNING|PREFERENCE):\s*/i, '')
44
+ .toLowerCase()
45
+ .split(/\s+/)
46
+ .filter((w) => w.length > 3)
47
+ .slice(0, 3);
48
+
49
+ for (const word of titleWords) keys.add(word);
50
+
51
+ return Array.from(keys);
52
+ }
53
+
54
+ function domainOverlap(keysA, keysB) {
55
+ const setB = new Set(keysB);
56
+ let overlap = 0;
57
+ for (const key of keysA) {
58
+ if (setB.has(key)) overlap++;
59
+ }
60
+ return overlap;
61
+ }
62
+
63
+ function inferPrompt(error, learning) {
64
+ const shared = (error.tags || []).filter((t) => (learning.tags || []).includes(t));
65
+ if (shared.length > 0) {
66
+ return `Task domain: ${shared.join(', ')}. How should the agent handle this scenario?`;
67
+ }
68
+
69
+ const scenario = (error.title || '').replace(/^(MISTAKE|ERROR):\s*/i, '').trim();
70
+ if (scenario) return `Scenario: ${scenario}. What is the better response?`;
71
+ return 'How should the agent respond in this situation?';
72
+ }
73
+
74
+ function getRubricWeightedScore(memory) {
75
+ if (!memory || !memory.rubricSummary) return null;
76
+ const score = Number(memory.rubricSummary.weightedScore);
77
+ if (!Number.isFinite(score)) return null;
78
+ return score;
79
+ }
80
+
81
+ function buildRubricDelta(error, learning) {
82
+ const errorScore = getRubricWeightedScore(error);
83
+ const learningScore = getRubricWeightedScore(learning);
84
+ if (errorScore == null && learningScore == null) return null;
85
+ const delta = (learningScore != null && errorScore != null)
86
+ ? Math.round((learningScore - errorScore) * 1000) / 1000
87
+ : null;
88
+ return {
89
+ learningWeightedScore: learningScore,
90
+ errorWeightedScore: errorScore,
91
+ weightedDelta: delta,
92
+ errorFailingCriteria: error && error.rubricSummary ? error.rubricSummary.failingCriteria || [] : [],
93
+ learningFailingCriteria: learning && learning.rubricSummary ? learning.rubricSummary.failingCriteria || [] : [],
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Find distractor errors for a learning: same-domain errors that are NOT
99
+ * the best match but look similar (high overlap, low rubric delta).
100
+ * These are harder negatives that train retrieval precision.
101
+ */
102
+ function findDistractors(learning, errorKeys, usedErrorId, maxDistractors) {
103
+ const learningKeys = extractDomainKeys(learning);
104
+ return errorKeys
105
+ .filter((err) => err.memory.id !== usedErrorId)
106
+ .map((err) => {
107
+ const overlap = domainOverlap(err.keys, learningKeys);
108
+ return { memory: err.memory, overlap };
109
+ })
110
+ .filter((d) => d.overlap > 0)
111
+ .sort((a, b) => b.overlap - a.overlap)
112
+ .slice(0, maxDistractors)
113
+ .map((d) => ({
114
+ id: d.memory.id,
115
+ title: d.memory.title,
116
+ content: d.memory.content,
117
+ overlap: d.overlap,
118
+ }));
119
+ }
120
+
121
+ function buildDpoPairs(errors, learnings, { maxDistractors = 2 } = {}) {
122
+ const pairs = [];
123
+ const usedErrors = new Set();
124
+ const usedLearnings = new Set();
125
+
126
+ const errorKeys = errors.map((e) => ({ memory: e, keys: extractDomainKeys(e) }));
127
+ const learningKeys = learnings.map((l) => ({ memory: l, keys: extractDomainKeys(l) }));
128
+
129
+ for (const err of errorKeys) {
130
+ let best = null;
131
+ let bestScore = 0;
132
+ let bestOverlap = 0;
133
+
134
+ for (const learn of learningKeys) {
135
+ if (usedLearnings.has(learn.memory.id)) continue;
136
+ const overlap = domainOverlap(err.keys, learn.keys);
137
+ const rubric = buildRubricDelta(err.memory, learn.memory);
138
+ const rubricDelta = rubric && rubric.weightedDelta != null ? rubric.weightedDelta : 0;
139
+ const score = overlap + Math.max(0, rubricDelta);
140
+ if (score > bestScore) {
141
+ best = learn;
142
+ bestScore = score;
143
+ bestOverlap = overlap;
144
+ }
145
+ }
146
+
147
+ if (best && bestScore > 0 && bestOverlap > 0) {
148
+ const distractors = findDistractors(best.memory, errorKeys, err.memory.id, maxDistractors);
149
+ pairs.push({
150
+ prompt: inferPrompt(err.memory, best.memory),
151
+ chosen: best.memory.content,
152
+ rejected: err.memory.content,
153
+ distractors: distractors.length > 0 ? distractors : undefined,
154
+ metadata: {
155
+ errorId: err.memory.id,
156
+ learningId: best.memory.id,
157
+ matchScore: bestScore,
158
+ overlapScore: domainOverlap(err.keys, best.keys),
159
+ matchedKeys: err.keys.filter((k) => best.keys.includes(k)),
160
+ errorTitle: err.memory.title,
161
+ learningTitle: best.memory.title,
162
+ rubric: buildRubricDelta(err.memory, best.memory),
163
+ distractorCount: distractors.length,
164
+ },
165
+ });
166
+ usedErrors.add(err.memory.id);
167
+ usedLearnings.add(best.memory.id);
168
+ }
169
+ }
170
+
171
+ return {
172
+ pairs,
173
+ unpairedErrors: errors.filter((e) => !usedErrors.has(e.id)),
174
+ unpairedLearnings: learnings.filter((l) => !usedLearnings.has(l.id)),
175
+ };
176
+ }
177
+
178
+ function toJSONL(pairs) {
179
+ return `${pairs.map((p) => JSON.stringify(p)).join('\n')}\n`;
180
+ }
181
+
182
+ function exportDpoFromMemories(memories) {
183
+ const errors = memories.filter((m) => m.category === 'error');
184
+ const learnings = memories.filter((m) => m.category === 'learning');
185
+ const result = buildDpoPairs(errors, learnings);
186
+
187
+ const traces = result.pairs.map((pair) => traceForDpoPair(pair));
188
+ const reasoning = aggregateTraces(traces);
189
+
190
+ const pairsWithTraces = result.pairs.map((pair, i) => ({
191
+ ...pair,
192
+ metadata: {
193
+ ...pair.metadata,
194
+ reasoningTrace: {
195
+ traceId: traces[i].traceId,
196
+ confidence: traces[i].summary.confidence,
197
+ passed: traces[i].summary.passed,
198
+ verified: traces[i].summary.verified,
199
+ refuted: traces[i].summary.refuted,
200
+ edgeCases: traces[i].edgeCases,
201
+ },
202
+ },
203
+ }));
204
+
205
+ return {
206
+ pairs: pairsWithTraces,
207
+ unpairedErrors: result.unpairedErrors,
208
+ unpairedLearnings: result.unpairedLearnings,
209
+ errors,
210
+ learnings,
211
+ reasoning,
212
+ jsonl: toJSONL(pairsWithTraces),
213
+ };
214
+ }
215
+
216
+ function parseArgs(argv) {
217
+ const args = {};
218
+ argv.forEach((arg) => {
219
+ if (!arg.startsWith('--')) return;
220
+ const [key, ...rest] = arg.slice(2).split('=');
221
+ args[key] = rest.length ? rest.join('=') : true;
222
+ });
223
+ return args;
224
+ }
225
+
226
+ function runCli() {
227
+ const args = parseArgs(process.argv.slice(2));
228
+
229
+ if (args.test) {
230
+ runTests();
231
+ return;
232
+ }
233
+
234
+ let memories = [];
235
+
236
+ if (args.input) {
237
+ const raw = fs.readFileSync(args.input, 'utf-8');
238
+ const parsed = JSON.parse(raw);
239
+ memories = Array.isArray(parsed) ? parsed : parsed.memories || [];
240
+ } else if (args['from-local']) {
241
+ memories = readJSONL(DEFAULT_LOCAL_MEMORY_LOG);
242
+ } else {
243
+ console.error('Provide --input=<path-to-json> or --from-local');
244
+ process.exit(1);
245
+ }
246
+
247
+ const result = exportDpoFromMemories(memories);
248
+ const jsonl = result.jsonl;
249
+
250
+ if (args.output) {
251
+ fs.writeFileSync(args.output, jsonl);
252
+ console.error(`Wrote ${result.pairs.length} DPO pairs to ${args.output}`);
253
+ } else {
254
+ process.stdout.write(jsonl);
255
+ }
256
+
257
+ console.error(`Errors=${result.errors.length} Learnings=${result.learnings.length} Pairs=${result.pairs.length}`);
258
+ console.error(`Unpaired errors=${result.unpairedErrors.length} Unpaired learnings=${result.unpairedLearnings.length}`);
259
+ }
260
+
261
+ function runTests() {
262
+ let passed = 0;
263
+ let failed = 0;
264
+
265
+ function assert(condition, name) {
266
+ if (condition) {
267
+ passed++;
268
+ console.log(` PASS ${name}`);
269
+ } else {
270
+ failed++;
271
+ console.log(` FAIL ${name}`);
272
+ }
273
+ }
274
+
275
+ console.log('\nexport-dpo-pairs.js tests\n');
276
+
277
+ const errors = [
278
+ {
279
+ id: 1,
280
+ title: 'MISTAKE: Claimed done with no test proof',
281
+ content: 'Claimed completion without running tests.',
282
+ category: 'error',
283
+ tags: ['verification', 'feedback'],
284
+ rubricSummary: {
285
+ weightedScore: 0.32,
286
+ failingCriteria: ['verification_evidence'],
287
+ failingGuardrails: ['testsPassed'],
288
+ },
289
+ },
290
+ {
291
+ id: 2,
292
+ title: 'MISTAKE: Generic mismatch',
293
+ content: 'No matching learning memory for this domain.',
294
+ category: 'error',
295
+ tags: ['unique-tag'],
296
+ },
297
+ ];
298
+
299
+ const learnings = [
300
+ {
301
+ id: 10,
302
+ title: 'SUCCESS: Always run tests before completion claims',
303
+ content: 'Run tests and include output before saying complete.',
304
+ category: 'learning',
305
+ tags: ['verification', 'feedback'],
306
+ rubricSummary: {
307
+ weightedScore: 0.89,
308
+ failingCriteria: [],
309
+ failingGuardrails: [],
310
+ },
311
+ },
312
+ ];
313
+
314
+ const result = buildDpoPairs(errors, learnings);
315
+ assert(result.pairs.length === 1, 'one pair built from overlapping domain keys');
316
+ assert(result.unpairedErrors.length === 1, 'unpaired error left when no match exists');
317
+ assert(result.unpairedLearnings.length === 0, 'no unpaired learnings');
318
+
319
+ const jsonl = toJSONL(result.pairs);
320
+ assert(jsonl.endsWith('\n'), 'JSONL output ends with newline');
321
+
322
+ const parsed = JSON.parse(jsonl.trim());
323
+ assert(parsed.prompt.includes('verification'), 'inferred prompt includes shared domain');
324
+ assert(parsed.metadata.rubric.weightedDelta > 0, 'rubric delta metadata is attached');
325
+
326
+ console.log(`\nResults: ${passed} passed, ${failed} failed\n`);
327
+ process.exit(failed > 0 ? 1 : 0);
328
+ }
329
+
330
+ module.exports = {
331
+ readJSONL,
332
+ extractDomainKeys,
333
+ domainOverlap,
334
+ inferPrompt,
335
+ buildRubricDelta,
336
+ findDistractors,
337
+ buildDpoPairs,
338
+ toJSONL,
339
+ exportDpoFromMemories,
340
+ DEFAULT_LOCAL_MEMORY_LOG,
341
+ };
342
+
343
+ if (require.main === module) {
344
+ runCli();
345
+ }