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,310 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * KTO (Kahneman-Tversky Optimization) Exporter
4
+ *
5
+ * Transforms binary up/down feedback into KTO JSONL records.
6
+ * Unlike DPO (which needs paired preferences), KTO works with
7
+ * individual binary signals — a natural fit for thumbs-up/down data.
8
+ *
9
+ * Output format per line:
10
+ * {"prompt": "...", "completion": "...", "label": true/false, "metadata": {...}}
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const PROJECT_ROOT = path.join(__dirname, '..');
17
+ const FEEDBACK_DIR = process.env.THUMBGATE_FEEDBACK_DIR || path.join(PROJECT_ROOT, '.claude', 'memory', 'feedback');
18
+ const DEFAULT_FEEDBACK_LOG = path.join(FEEDBACK_DIR, 'feedback-log.jsonl');
19
+ const DEFAULT_MEMORY_LOG = path.join(FEEDBACK_DIR, 'memory-log.jsonl');
20
+
21
+ function readJSONL(filePath) {
22
+ if (!fs.existsSync(filePath)) return [];
23
+ const raw = fs.readFileSync(filePath, 'utf-8').trim();
24
+ if (!raw) return [];
25
+ return raw
26
+ .split('\n')
27
+ .map((line) => {
28
+ try {
29
+ return JSON.parse(line);
30
+ } catch {
31
+ return null;
32
+ }
33
+ })
34
+ .filter(Boolean);
35
+ }
36
+
37
+ /**
38
+ * Infer a prompt string from a feedback entry.
39
+ * Uses context, tags, or domain info to reconstruct what was being asked.
40
+ */
41
+ function inferPrompt(entry) {
42
+ if (entry.context && entry.context.trim()) {
43
+ return entry.context.trim();
44
+ }
45
+ if (entry.richContext && entry.richContext.domain) {
46
+ return `Task domain: ${entry.richContext.domain}`;
47
+ }
48
+ if (Array.isArray(entry.tags) && entry.tags.length > 0) {
49
+ return `Task: ${entry.tags.join(', ')}`;
50
+ }
51
+ return 'General coding task';
52
+ }
53
+
54
+ /**
55
+ * Infer a completion string from a feedback entry.
56
+ * For positive: whatWorked or content describes the good response.
57
+ * For negative: whatWentWrong or whatToChange describes the bad response.
58
+ */
59
+ function inferCompletion(entry) {
60
+ const signal = normalizeSignal(entry.signal);
61
+ if (signal === 'positive') {
62
+ if (entry.whatWorked && entry.whatWorked.trim()) return entry.whatWorked.trim();
63
+ if (entry.content && entry.content.trim()) return entry.content.trim();
64
+ return 'Completed task successfully';
65
+ }
66
+ if (entry.whatWentWrong && entry.whatWentWrong.trim()) return entry.whatWentWrong.trim();
67
+ if (entry.whatToChange && entry.whatToChange.trim()) return entry.whatToChange.trim();
68
+ if (entry.content && entry.content.trim()) return entry.content.trim();
69
+ return 'Failed to complete task correctly';
70
+ }
71
+
72
+ function normalizeSignal(signal) {
73
+ const value = String(signal || '').trim().toLowerCase();
74
+ if (['up', 'thumbsup', 'thumbs-up', 'thumbs_up', 'positive', 'good'].includes(value)) return 'positive';
75
+ if (['down', 'thumbsdown', 'thumbs-down', 'thumbs_down', 'negative', 'bad'].includes(value)) return 'negative';
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * Build a single KTO record from a feedback or memory entry.
81
+ * Returns null if the entry lacks a valid signal.
82
+ */
83
+ function buildKtoRecord(entry) {
84
+ const signal = normalizeSignal(entry.signal);
85
+ if (!signal) return null;
86
+
87
+ const label = signal === 'positive';
88
+ const prompt = inferPrompt(entry);
89
+ const completion = inferCompletion(entry);
90
+
91
+ return {
92
+ prompt,
93
+ completion,
94
+ label,
95
+ metadata: {
96
+ sourceId: entry.id || null,
97
+ signal,
98
+ signalSource: entry.sourceFeedbackId ? 'memory-log' : 'feedback-log',
99
+ tags: entry.tags || [],
100
+ domain: (entry.richContext && entry.richContext.domain) || null,
101
+ outcomeCategory: (entry.richContext && entry.richContext.outcomeCategory) || null,
102
+ timestamp: entry.timestamp || null,
103
+ rubricScore: (entry.rubric && entry.rubric.weightedScore != null)
104
+ ? entry.rubric.weightedScore
105
+ : null,
106
+ },
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Build KTO records from an array of feedback/memory entries.
112
+ */
113
+ function buildKtoPairs(entries) {
114
+ const records = [];
115
+ const skipped = [];
116
+ for (const entry of entries) {
117
+ const record = buildKtoRecord(entry);
118
+ if (record) {
119
+ records.push(record);
120
+ } else {
121
+ skipped.push(entry);
122
+ }
123
+ }
124
+ return { records, skipped };
125
+ }
126
+
127
+ function toJSONL(records) {
128
+ if (records.length === 0) return '';
129
+ return `${records.map((r) => JSON.stringify(r)).join('\n')}\n`;
130
+ }
131
+
132
+ function exportKtoFromFeedback(feedbackEntries, memoryEntries) {
133
+ const all = [...feedbackEntries, ...memoryEntries];
134
+ // Deduplicate by id
135
+ const seen = new Set();
136
+ const unique = [];
137
+ for (const entry of all) {
138
+ const key = entry.id || JSON.stringify(entry);
139
+ if (!seen.has(key)) {
140
+ seen.add(key);
141
+ unique.push(entry);
142
+ }
143
+ }
144
+ const result = buildKtoPairs(unique);
145
+ return {
146
+ records: result.records,
147
+ skipped: result.skipped,
148
+ totalInput: unique.length,
149
+ jsonl: toJSONL(result.records),
150
+ };
151
+ }
152
+
153
+ function parseArgs(argv) {
154
+ const args = {};
155
+ argv.forEach((arg) => {
156
+ if (!arg.startsWith('--')) return;
157
+ const [key, ...rest] = arg.slice(2).split('=');
158
+ args[key] = rest.length ? rest.join('=') : true;
159
+ });
160
+ return args;
161
+ }
162
+
163
+ function runCli() {
164
+ const args = parseArgs(process.argv.slice(2));
165
+
166
+ if (args.test) {
167
+ runTests();
168
+ return;
169
+ }
170
+
171
+ let feedbackEntries = [];
172
+ let memoryEntries = [];
173
+
174
+ if (args.input) {
175
+ const raw = fs.readFileSync(args.input, 'utf-8');
176
+ const parsed = JSON.parse(raw);
177
+ feedbackEntries = Array.isArray(parsed) ? parsed : parsed.entries || [];
178
+ } else if (args['from-local']) {
179
+ feedbackEntries = readJSONL(DEFAULT_FEEDBACK_LOG);
180
+ memoryEntries = readJSONL(DEFAULT_MEMORY_LOG);
181
+ } else {
182
+ console.error('Provide --input=<path-to-json> or --from-local');
183
+ process.exit(1);
184
+ }
185
+
186
+ const result = exportKtoFromFeedback(feedbackEntries, memoryEntries);
187
+
188
+ if (args.output) {
189
+ fs.writeFileSync(args.output, result.jsonl);
190
+ console.error(`Wrote ${result.records.length} KTO records to ${args.output}`);
191
+ } else {
192
+ process.stdout.write(result.jsonl);
193
+ }
194
+
195
+ const positiveCount = result.records.filter((r) => r.label === true).length;
196
+ const negativeCount = result.records.filter((r) => r.label === false).length;
197
+ console.error(`Total=${result.totalInput} Exported=${result.records.length} Positive=${positiveCount} Negative=${negativeCount} Skipped=${result.skipped.length}`);
198
+ }
199
+
200
+ function runTests() {
201
+ let passed = 0;
202
+ let failed = 0;
203
+
204
+ function assert(condition, name) {
205
+ if (condition) {
206
+ passed++;
207
+ console.log(` PASS ${name}`);
208
+ } else {
209
+ failed++;
210
+ console.log(` FAIL ${name}`);
211
+ }
212
+ }
213
+
214
+ console.log('\nexport-kto-pairs.js tests\n');
215
+
216
+ // Test 1: positive signal produces label true
217
+ const pos = buildKtoRecord({
218
+ id: 'fb_1',
219
+ signal: 'positive',
220
+ context: 'Implemented auth',
221
+ whatWorked: 'JWT tokens with refresh rotation',
222
+ tags: ['auth'],
223
+ timestamp: '2025-01-01T00:00:00Z',
224
+ });
225
+ assert(pos !== null, 'positive signal produces a record');
226
+ assert(pos.label === true, 'positive signal produces label: true');
227
+
228
+ // Test 2: negative signal produces label false
229
+ const neg = buildKtoRecord({
230
+ id: 'fb_2',
231
+ signal: 'negative',
232
+ context: 'Tried to deploy',
233
+ whatWentWrong: 'Missing env vars',
234
+ tags: ['deploy'],
235
+ timestamp: '2025-01-01T00:00:00Z',
236
+ });
237
+ assert(neg !== null, 'negative signal produces a record');
238
+ assert(neg.label === false, 'negative signal produces label: false');
239
+
240
+ // Test 3: missing context handled gracefully
241
+ const noCtx = buildKtoRecord({
242
+ id: 'fb_3',
243
+ signal: 'up',
244
+ tags: ['testing'],
245
+ });
246
+ assert(noCtx !== null, 'entry with missing context still produces record');
247
+ assert(noCtx.prompt === 'Task: testing', 'missing context falls back to tags');
248
+
249
+ // Test 4: invalid signal returns null
250
+ const invalid = buildKtoRecord({ id: 'fb_4', signal: 'maybe' });
251
+ assert(invalid === null, 'invalid signal returns null');
252
+
253
+ // Test 5: JSONL output is valid
254
+ const records = [pos, neg];
255
+ const jsonl = toJSONL(records);
256
+ const lines = jsonl.trim().split('\n');
257
+ let allValid = true;
258
+ for (const line of lines) {
259
+ try {
260
+ JSON.parse(line);
261
+ } catch {
262
+ allValid = false;
263
+ }
264
+ }
265
+ assert(allValid, 'JSONL output is valid JSON per line');
266
+ assert(jsonl.endsWith('\n'), 'JSONL output ends with newline');
267
+
268
+ // Test 6: metadata includes signal source and timestamp
269
+ assert(pos.metadata.signalSource === 'feedback-log', 'metadata includes signal source');
270
+ assert(pos.metadata.timestamp === '2025-01-01T00:00:00Z', 'metadata includes timestamp');
271
+ assert(pos.metadata.signal === 'positive', 'metadata includes normalized signal');
272
+
273
+ // Test 7: empty context with richContext domain
274
+ const richCtx = buildKtoRecord({
275
+ id: 'fb_5',
276
+ signal: 'up',
277
+ richContext: { domain: 'security', outcomeCategory: 'quick-success' },
278
+ });
279
+ assert(richCtx.prompt === 'Task domain: security', 'richContext domain used as prompt fallback');
280
+ assert(richCtx.metadata.domain === 'security', 'metadata captures domain');
281
+
282
+ // Test 8: buildKtoPairs filters bad entries
283
+ const result = buildKtoPairs([
284
+ { id: 'a', signal: 'up', context: 'good' },
285
+ { id: 'b', signal: 'invalid' },
286
+ { id: 'c', signal: 'down', context: 'bad', whatWentWrong: 'broke it' },
287
+ ]);
288
+ assert(result.records.length === 2, 'buildKtoPairs keeps valid entries');
289
+ assert(result.skipped.length === 1, 'buildKtoPairs tracks skipped entries');
290
+
291
+ console.log(`\nResults: ${passed} passed, ${failed} failed\n`);
292
+ process.exit(failed > 0 ? 1 : 0);
293
+ }
294
+
295
+ module.exports = {
296
+ readJSONL,
297
+ normalizeSignal,
298
+ inferPrompt,
299
+ inferCompletion,
300
+ buildKtoRecord,
301
+ buildKtoPairs,
302
+ toJSONL,
303
+ exportKtoFromFeedback,
304
+ DEFAULT_FEEDBACK_LOG,
305
+ DEFAULT_MEMORY_LOG,
306
+ };
307
+
308
+ if (require.main === module) {
309
+ runCli();
310
+ }