thumbgate 0.9.9

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 (369) 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/rlhf-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 +1483 -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 +286 -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 +1195 -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-314.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 +293 -0
  117. package/scripts/auto-wire-hooks.js +316 -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 +93 -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 +231 -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 +206 -0
  152. package/scripts/ensure-repo-bootstrap.js +129 -0
  153. package/scripts/ephemeral-agent-store.js +219 -0
  154. package/scripts/eval-harness.js +56 -0
  155. package/scripts/evolution-state.js +241 -0
  156. package/scripts/experiment-tracker.js +267 -0
  157. package/scripts/export-databricks-bundle.js +242 -0
  158. package/scripts/export-dpo-pairs.js +344 -0
  159. package/scripts/export-kto-pairs.js +309 -0
  160. package/scripts/export-training.js +450 -0
  161. package/scripts/failure-diagnostics.js +558 -0
  162. package/scripts/feedback-attribution.js +313 -0
  163. package/scripts/feedback-fallback.js +110 -0
  164. package/scripts/feedback-history-distiller.js +391 -0
  165. package/scripts/feedback-inbox-read.js +162 -0
  166. package/scripts/feedback-loop.js +1887 -0
  167. package/scripts/feedback-paths.js +145 -0
  168. package/scripts/feedback-quality.js +139 -0
  169. package/scripts/feedback-root-consolidator.js +238 -0
  170. package/scripts/feedback-schema.js +426 -0
  171. package/scripts/feedback-session.js +286 -0
  172. package/scripts/feedback-to-memory.js +185 -0
  173. package/scripts/feedback-to-rules.js +164 -0
  174. package/scripts/filesystem-search.js +405 -0
  175. package/scripts/funnel-analytics.js +35 -0
  176. package/scripts/gate-satisfy.js +42 -0
  177. package/scripts/gate-stats.js +116 -0
  178. package/scripts/gate-templates.js +70 -0
  179. package/scripts/gates-engine.js +816 -0
  180. package/scripts/generate-paperbanana-diagrams.sh +99 -0
  181. package/scripts/generate-pretool-hook.sh +40 -0
  182. package/scripts/github-about.js +350 -0
  183. package/scripts/github-outreach.js +65 -0
  184. package/scripts/gtm-revenue-loop.js +520 -0
  185. package/scripts/hallucination-detector.js +226 -0
  186. package/scripts/hf-papers.js +317 -0
  187. package/scripts/history-distiller.js +200 -0
  188. package/scripts/hook-auto-capture.sh +100 -0
  189. package/scripts/hook-stop-pr-thread-check.sh +68 -0
  190. package/scripts/hook-stop-self-score.sh +51 -0
  191. package/scripts/hook-stop-verify-deploy.sh +31 -0
  192. package/scripts/hook-thumbgate-cache-updater.js +48 -0
  193. package/scripts/hook-verify-before-done.sh +20 -0
  194. package/scripts/hosted-config.js +156 -0
  195. package/scripts/hybrid-feedback-context.js +675 -0
  196. package/scripts/install-mcp.js +159 -0
  197. package/scripts/intent-router.js +392 -0
  198. package/scripts/internal-agent-bootstrap.js +490 -0
  199. package/scripts/jsonl-watcher.js +155 -0
  200. package/scripts/lesson-db.js +613 -0
  201. package/scripts/lesson-inference.js +310 -0
  202. package/scripts/lesson-retrieval.js +95 -0
  203. package/scripts/lesson-rotation.js +137 -0
  204. package/scripts/lesson-search.js +644 -0
  205. package/scripts/lesson-synthesis.js +196 -0
  206. package/scripts/license.js +50 -0
  207. package/scripts/local-model-profile.js +384 -0
  208. package/scripts/markdown-escape.js +12 -0
  209. package/scripts/marketing-experiment.js +671 -0
  210. package/scripts/mcp-config.js +149 -0
  211. package/scripts/mcp-policy.js +99 -0
  212. package/scripts/memalign-recall.js +111 -0
  213. package/scripts/memory-firewall.js +222 -0
  214. package/scripts/memory-migration.js +296 -0
  215. package/scripts/meta-policy.js +190 -0
  216. package/scripts/metered-billing.js +16 -0
  217. package/scripts/model-tier-router.js +301 -0
  218. package/scripts/money-watcher.js +71 -0
  219. package/scripts/multi-hop-recall.js +240 -0
  220. package/scripts/natural-language-harness.js +330 -0
  221. package/scripts/obsidian-export.js +713 -0
  222. package/scripts/operational-dashboard.js +103 -0
  223. package/scripts/operational-summary.js +93 -0
  224. package/scripts/optimize-context.js +17 -0
  225. package/scripts/org-dashboard.js +201 -0
  226. package/scripts/partner-orchestration.js +146 -0
  227. package/scripts/per-step-scoring.js +165 -0
  228. package/scripts/perplexity-marketing.js +466 -0
  229. package/scripts/pii-scanner.js +153 -0
  230. package/scripts/plan-gate.js +154 -0
  231. package/scripts/post-everywhere.js +308 -0
  232. package/scripts/post-to-x-retry.sh +22 -0
  233. package/scripts/post-to-x.js +369 -0
  234. package/scripts/pr-manager.js +236 -0
  235. package/scripts/predictive-insights.js +356 -0
  236. package/scripts/principle-extractor.js +162 -0
  237. package/scripts/pro-features.js +40 -0
  238. package/scripts/pro-local-dashboard.js +174 -0
  239. package/scripts/problem-detail.js +53 -0
  240. package/scripts/product-feedback.js +134 -0
  241. package/scripts/profile-router.js +245 -0
  242. package/scripts/prompt-dlp.js +221 -0
  243. package/scripts/prompt-guard.js +83 -0
  244. package/scripts/prove-adapters.js +863 -0
  245. package/scripts/prove-attribution.js +365 -0
  246. package/scripts/prove-automation.js +653 -0
  247. package/scripts/prove-autoresearch.js +304 -0
  248. package/scripts/prove-claim-verification.js +277 -0
  249. package/scripts/prove-cloudflare-sandbox.js +163 -0
  250. package/scripts/prove-data-pipeline.js +410 -0
  251. package/scripts/prove-data-quality.js +227 -0
  252. package/scripts/prove-evolution.js +352 -0
  253. package/scripts/prove-harnesses.js +287 -0
  254. package/scripts/prove-intelligence.js +259 -0
  255. package/scripts/prove-lancedb.js +371 -0
  256. package/scripts/prove-local-intelligence.js +342 -0
  257. package/scripts/prove-loop-closure.js +263 -0
  258. package/scripts/prove-predictive-insights.js +357 -0
  259. package/scripts/prove-runtime.js +350 -0
  260. package/scripts/prove-seo-gsd.js +234 -0
  261. package/scripts/prove-settings.js +279 -0
  262. package/scripts/prove-subway-upgrades.js +277 -0
  263. package/scripts/prove-tessl.js +229 -0
  264. package/scripts/prove-training-export.js +327 -0
  265. package/scripts/prove-workflow-contract.js +116 -0
  266. package/scripts/prove-xmemory.js +332 -0
  267. package/scripts/publish-decision.js +133 -0
  268. package/scripts/pulse.js +80 -0
  269. package/scripts/rate-limiter.js +125 -0
  270. package/scripts/reddit-dm-outreach.js +182 -0
  271. package/scripts/reddit-monitor-cron.sh +26 -0
  272. package/scripts/reflector-agent.js +221 -0
  273. package/scripts/reminder-engine.js +132 -0
  274. package/scripts/revenue-status.js +472 -0
  275. package/scripts/risk-scorer.js +459 -0
  276. package/scripts/rlaif-self-audit.js +129 -0
  277. package/scripts/rlhf_session_start.sh +32 -0
  278. package/scripts/rubric-engine.js +230 -0
  279. package/scripts/schedule-manager.js +251 -0
  280. package/scripts/secret-scanner.js +414 -0
  281. package/scripts/self-heal.js +147 -0
  282. package/scripts/self-healing-check.js +188 -0
  283. package/scripts/semantic-layer.js +98 -0
  284. package/scripts/seo-gsd.js +1153 -0
  285. package/scripts/settings-hierarchy.js +214 -0
  286. package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
  287. package/scripts/skill-exporter.js +262 -0
  288. package/scripts/skill-generator.js +446 -0
  289. package/scripts/skill-materializer.js +134 -0
  290. package/scripts/skill-packs.js +136 -0
  291. package/scripts/skill-proposer.js +99 -0
  292. package/scripts/skill-quality-tracker.js +282 -0
  293. package/scripts/slo-alert-engine.js +14 -0
  294. package/scripts/slow-loop.js +72 -0
  295. package/scripts/social-analytics/db/schema.sql +32 -0
  296. package/scripts/social-analytics/db/social-analytics.db +0 -0
  297. package/scripts/social-analytics/digest.js +256 -0
  298. package/scripts/social-analytics/generate-instagram-card.js +97 -0
  299. package/scripts/social-analytics/instagram-thumbgate-post.js +107 -0
  300. package/scripts/social-analytics/load-env.js +46 -0
  301. package/scripts/social-analytics/mcp-server.js +289 -0
  302. package/scripts/social-analytics/normalizer.js +580 -0
  303. package/scripts/social-analytics/notify.js +162 -0
  304. package/scripts/social-analytics/poll-all.js +92 -0
  305. package/scripts/social-analytics/pollers/github.js +195 -0
  306. package/scripts/social-analytics/pollers/instagram.js +253 -0
  307. package/scripts/social-analytics/pollers/linkedin.js +330 -0
  308. package/scripts/social-analytics/pollers/plausible.js +247 -0
  309. package/scripts/social-analytics/pollers/reddit.js +306 -0
  310. package/scripts/social-analytics/pollers/threads.js +233 -0
  311. package/scripts/social-analytics/pollers/tiktok.js +203 -0
  312. package/scripts/social-analytics/pollers/x.js +227 -0
  313. package/scripts/social-analytics/pollers/youtube.js +304 -0
  314. package/scripts/social-analytics/pollers/zernio.js +183 -0
  315. package/scripts/social-analytics/publish-instagram-thumbgate.js +98 -0
  316. package/scripts/social-analytics/publish-thumbgate-launch.js +316 -0
  317. package/scripts/social-analytics/publishers/devto.js +122 -0
  318. package/scripts/social-analytics/publishers/instagram.js +317 -0
  319. package/scripts/social-analytics/publishers/linkedin.js +294 -0
  320. package/scripts/social-analytics/publishers/reddit.js +390 -0
  321. package/scripts/social-analytics/publishers/threads.js +275 -0
  322. package/scripts/social-analytics/publishers/tiktok.js +217 -0
  323. package/scripts/social-analytics/publishers/x.js +259 -0
  324. package/scripts/social-analytics/publishers/youtube.js +223 -0
  325. package/scripts/social-analytics/publishers/zernio.js +378 -0
  326. package/scripts/social-analytics/run-digest.js +34 -0
  327. package/scripts/social-analytics/store.js +257 -0
  328. package/scripts/social-analytics/utm.js +143 -0
  329. package/scripts/social-pipeline.js +2628 -0
  330. package/scripts/social-quality-gate.js +18 -0
  331. package/scripts/social-reply-monitor.js +445 -0
  332. package/scripts/status-dashboard.js +155 -0
  333. package/scripts/statusline-lesson.js +16 -0
  334. package/scripts/statusline-tower.js +8 -0
  335. package/scripts/statusline.sh +116 -0
  336. package/scripts/stripe-live-status.js +115 -0
  337. package/scripts/subagent-profiles.js +79 -0
  338. package/scripts/sync-gh-secrets-from-env.sh +70 -0
  339. package/scripts/sync-github-about.js +52 -0
  340. package/scripts/sync-version.js +447 -0
  341. package/scripts/synthetic-dpo.js +234 -0
  342. package/scripts/telemetry-analytics.js +821 -0
  343. package/scripts/tessl-export.js +371 -0
  344. package/scripts/test-coverage.js +120 -0
  345. package/scripts/thompson-sampling.js +417 -0
  346. package/scripts/thumbgate-search.js +189 -0
  347. package/scripts/tool-kpi-tracker.js +12 -0
  348. package/scripts/tool-registry.js +811 -0
  349. package/scripts/train_from_feedback.py +933 -0
  350. package/scripts/user-profile.js +78 -0
  351. package/scripts/validate-feedback.js +581 -0
  352. package/scripts/validate-workflow-contract.js +287 -0
  353. package/scripts/vector-store.js +197 -0
  354. package/scripts/verification-loop.js +291 -0
  355. package/scripts/verify-obsidian-setup.sh +269 -0
  356. package/scripts/verify-run.js +269 -0
  357. package/scripts/webhook-delivery.js +62 -0
  358. package/scripts/weekly-auto-post.js +124 -0
  359. package/scripts/workflow-runs.js +154 -0
  360. package/scripts/workflow-sprint-intake.js +475 -0
  361. package/scripts/workspace-evolver.js +374 -0
  362. package/scripts/x-autonomous-marketing.js +139 -0
  363. package/scripts/xmemory-lite.js +405 -0
  364. package/skills/agent-memory/SKILL.md +97 -0
  365. package/skills/rlhf-feedback/SKILL.md +49 -0
  366. package/skills/solve-architecture-autonomy/SKILL.md +17 -0
  367. package/skills/solve-architecture-autonomy/tool.js +33 -0
  368. package/skills/thumbgate/SKILL.md +114 -0
  369. package/src/api/server.js +4206 -0
@@ -0,0 +1,450 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Training Data Exporter
4
+ *
5
+ * Exports feedback data in multiple formats for ML training pipelines:
6
+ * - PyTorch JSON (XPRT-01): prompt/chosen/rejected preference pairs
7
+ * - CSV summary (XPRT-02): one row per feedback entry with column headers
8
+ * - Action analysis report (XPRT-03): tool call patterns, success rates, top failures
9
+ * - DPO validation gate (XPRT-04): validateMemoryStructure() prevents bad training pairs
10
+ *
11
+ * Ported and adapted from Subway_RN_Demo exportTrainingData() patterns.
12
+ * PATH: PROJECT_ROOT = path.join(__dirname, '..') — 1 level from scripts/
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const { resolveFeedbackDir } = require('./feedback-paths');
20
+
21
+ const SEQUENCE_WINDOW = 10;
22
+
23
+ function getFeedbackDir() {
24
+ return resolveFeedbackDir();
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Utility helpers
29
+ // ---------------------------------------------------------------------------
30
+
31
+ function readJSONL(filePath) {
32
+ if (!fs.existsSync(filePath)) return [];
33
+ const raw = fs.readFileSync(filePath, 'utf-8').trim();
34
+ if (!raw) return [];
35
+ return raw
36
+ .split('\n')
37
+ .map((line) => {
38
+ try { return JSON.parse(line); } catch { return null; }
39
+ })
40
+ .filter(Boolean);
41
+ }
42
+
43
+ function ensureDir(dirPath) {
44
+ if (!fs.existsSync(dirPath)) {
45
+ fs.mkdirSync(dirPath, { recursive: true });
46
+ }
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // XPRT-04: validateMemoryStructure
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /**
54
+ * Gate function: validates a DPO memory pair has the required fields
55
+ * before allowing it into training export.
56
+ *
57
+ * Required: title, content, category, tags (at least 1 non-generic).
58
+ * For DPO pairs specifically: must have a 'chosen' direction (prompt + chosen + rejected).
59
+ *
60
+ * @param {object} memory - DPO memory entry
61
+ * @returns {{ valid: boolean, issues: string[] }}
62
+ */
63
+ function validateMemoryStructure(memory) {
64
+ const issues = [];
65
+
66
+ if (!memory || typeof memory !== 'object') {
67
+ return { valid: false, issues: ['memory must be a non-null object'] };
68
+ }
69
+
70
+ // Required fields
71
+ if (!memory.title || typeof memory.title !== 'string') {
72
+ issues.push('title: required string');
73
+ }
74
+ if (!memory.content || typeof memory.content !== 'string') {
75
+ issues.push('content: required string');
76
+ } else if (memory.content.length < 10) {
77
+ issues.push(`content: too short (${memory.content.length} chars, min 10)`);
78
+ }
79
+ if (!memory.category) {
80
+ issues.push('category: required');
81
+ }
82
+ if (!Array.isArray(memory.tags) || memory.tags.length === 0) {
83
+ issues.push('tags: at least 1 tag required');
84
+ }
85
+
86
+ // DPO-specific: requires 'chosen' field for preference pair export
87
+ // If exporting as DPO pair, must have prompt + chosen + rejected
88
+ if (memory._dpoExport) {
89
+ if (!memory.prompt) issues.push('DPO export: prompt field required');
90
+ if (!memory.chosen) issues.push('DPO export: chosen field required');
91
+ if (!memory.rejected) issues.push('DPO export: rejected field required');
92
+ }
93
+
94
+ return { valid: issues.length === 0, issues };
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // XPRT-01: PyTorch JSON export
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Build preference pairs from feedback log.
103
+ * Matches positive entries as 'chosen' and negative entries as 'rejected'
104
+ * when they share the same context domain/tags.
105
+ *
106
+ * @param {object[]} feedbackEntries - Raw feedback log entries
107
+ * @returns {object[]} Array of { prompt, chosen, rejected } pairs
108
+ */
109
+ function buildPreferencePairs(feedbackEntries) {
110
+ const positive = feedbackEntries.filter(
111
+ (f) => f.signal === 'positive' || f.feedback === 'up'
112
+ );
113
+ const negative = feedbackEntries.filter(
114
+ (f) => f.signal === 'negative' || f.feedback === 'down'
115
+ );
116
+
117
+ const pairs = [];
118
+ const usedNeg = new Set();
119
+
120
+ for (const pos of positive) {
121
+ // Find a negative entry with overlapping tags (domain similarity)
122
+ const posTags = new Set(pos.tags || []);
123
+ let bestNegIdx = -1;
124
+ let bestOverlap = -1;
125
+
126
+ for (let i = 0; i < negative.length; i++) {
127
+ if (usedNeg.has(i)) continue;
128
+ const negTags = new Set(negative[i].tags || []);
129
+ let overlap = 0;
130
+ for (const t of posTags) {
131
+ if (negTags.has(t)) overlap++;
132
+ }
133
+ if (overlap > bestOverlap) {
134
+ bestOverlap = overlap;
135
+ bestNegIdx = i;
136
+ }
137
+ }
138
+
139
+ // If no tag overlap, still pair with any unused negative
140
+ if (bestNegIdx === -1) {
141
+ for (let i = 0; i < negative.length; i++) {
142
+ if (!usedNeg.has(i)) { bestNegIdx = i; break; }
143
+ }
144
+ }
145
+
146
+ if (bestNegIdx >= 0) {
147
+ usedNeg.add(bestNegIdx);
148
+ const neg = negative[bestNegIdx];
149
+ pairs.push({
150
+ prompt: pos.context || pos.richContext?.description || '',
151
+ chosen: (pos.richContext?.outcomeCategory || 'positive') + ': ' + (pos.context || ''),
152
+ rejected: (neg.richContext?.outcomeCategory || 'negative') + ': ' + (neg.context || ''),
153
+ metadata: {
154
+ posId: pos.id,
155
+ negId: neg.id,
156
+ posTags: pos.tags || [],
157
+ negTags: neg.tags || [],
158
+ tagOverlap: bestOverlap,
159
+ },
160
+ });
161
+ }
162
+ }
163
+
164
+ return pairs;
165
+ }
166
+
167
+ /**
168
+ * Export feedback data as PyTorch-compatible JSON.
169
+ *
170
+ * Format: { metadata: { ... }, sequences: [{ X: {...}, y, label }] }
171
+ * Each sequence contains rewardSequence, trend, timeGaps features.
172
+ *
173
+ * @param {string} [feedbackDir] - Override feedback directory
174
+ * @param {string} [outputPath] - Override output path
175
+ * @returns {{ outputPath: string, pairCount: number, sequenceCount: number }}
176
+ */
177
+ function exportPyTorchJSON(feedbackDir, outputPath) {
178
+ const fbDir = feedbackDir || getFeedbackDir();
179
+ const feedbackPath = path.join(fbDir, 'feedback-log.jsonl');
180
+ const sequencePath = path.join(fbDir, 'feedback-sequences.jsonl');
181
+ const exportDir = path.join(fbDir, 'training-data');
182
+
183
+ ensureDir(exportDir);
184
+
185
+ const feedbackEntries = readJSONL(feedbackPath);
186
+ const sequences = readJSONL(sequencePath);
187
+ const pairs = buildPreferencePairs(feedbackEntries);
188
+
189
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
190
+ const outPath = outputPath || path.join(exportDir, `training-pytorch-${timestamp}.json`);
191
+
192
+ const pytorchData = {
193
+ metadata: {
194
+ exportDate: new Date().toISOString(),
195
+ format: 'pytorch-dpo',
196
+ pairCount: pairs.length,
197
+ sequenceCount: sequences.length,
198
+ windowSize: SEQUENCE_WINDOW,
199
+ features: ['rewardSequence', 'recentTrend', 'timeGaps'],
200
+ },
201
+ // DPO preference pairs (prompt/chosen/rejected)
202
+ pairs: pairs.map((p) => ({
203
+ prompt: p.prompt,
204
+ chosen: p.chosen,
205
+ rejected: p.rejected,
206
+ })),
207
+ // Raw sequences for LSTM/Transformer training
208
+ sequences: sequences.map((s) => ({
209
+ X: {
210
+ rewardSequence: (s.features && s.features.rewardSequence) || [],
211
+ trend: (s.features && s.features.recentTrend) || 0,
212
+ timeGaps: (s.features && s.features.timeGaps) || [],
213
+ },
214
+ y: s.targetReward,
215
+ label: s.label,
216
+ })),
217
+ };
218
+
219
+ fs.writeFileSync(outPath, JSON.stringify(pytorchData, null, 2));
220
+ return { outputPath: outPath, pairCount: pairs.length, sequenceCount: sequences.length };
221
+ }
222
+
223
+ // ---------------------------------------------------------------------------
224
+ // XPRT-02: CSV summary export
225
+ // ---------------------------------------------------------------------------
226
+
227
+ /**
228
+ * Escape a CSV field value (wrap in quotes if contains comma/quote/newline).
229
+ *
230
+ * @param {*} value
231
+ * @returns {string}
232
+ */
233
+ function escapeCsvField(value) {
234
+ const str = String(value == null ? '' : value);
235
+ if (str.includes(',') || str.includes('"') || str.includes('\n')) {
236
+ return '"' + str.replace(/"/g, '""') + '"';
237
+ }
238
+ return str;
239
+ }
240
+
241
+ /**
242
+ * Export feedback entries as CSV with standard headers.
243
+ *
244
+ * Columns: id, timestamp, signal, reward, context, domain, tags, outcomeCategory
245
+ *
246
+ * @param {string} [feedbackDir] - Override feedback directory
247
+ * @param {string} [outputPath] - Override output path
248
+ * @returns {{ outputPath: string, rowCount: number }}
249
+ */
250
+ function exportCSV(feedbackDir, outputPath) {
251
+ const fbDir = feedbackDir || getFeedbackDir();
252
+ const feedbackPath = path.join(fbDir, 'feedback-log.jsonl');
253
+ const exportDir = path.join(fbDir, 'training-data');
254
+
255
+ ensureDir(exportDir);
256
+
257
+ const entries = readJSONL(feedbackPath);
258
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
259
+ const outPath = outputPath || path.join(exportDir, `training-summary-${timestamp}.csv`);
260
+
261
+ const headers = ['id', 'timestamp', 'signal', 'reward', 'context', 'domain', 'tags', 'outcomeCategory'];
262
+ const rows = entries.map((e) => [
263
+ e.id || '',
264
+ e.timestamp || '',
265
+ e.signal || e.feedback || '',
266
+ e.reward != null ? e.reward : '',
267
+ e.context || '',
268
+ (e.richContext && e.richContext.domain) || '',
269
+ Array.isArray(e.tags) ? e.tags.join(';') : '',
270
+ (e.richContext && e.richContext.outcomeCategory) || '',
271
+ ].map(escapeCsvField).join(','));
272
+
273
+ const csv = [headers.join(','), ...rows].join('\n');
274
+ fs.writeFileSync(outPath, csv);
275
+
276
+ return { outputPath: outPath, rowCount: entries.length };
277
+ }
278
+
279
+ // ---------------------------------------------------------------------------
280
+ // XPRT-03: Action analysis report
281
+ // ---------------------------------------------------------------------------
282
+
283
+ /**
284
+ * Analyze action patterns from sequences and feedback entries.
285
+ *
286
+ * Produces: tool call frequency, success rates, top failure modes.
287
+ *
288
+ * @param {string} [feedbackDir] - Override feedback directory
289
+ * @param {string} [outputPath] - Override output path
290
+ * @returns {{ outputPath: string, report: object }}
291
+ */
292
+ function exportActionAnalysis(feedbackDir, outputPath) {
293
+ const fbDir = feedbackDir || getFeedbackDir();
294
+ const feedbackPath = path.join(fbDir, 'feedback-log.jsonl');
295
+ const sequencePath = path.join(fbDir, 'feedback-sequences.jsonl');
296
+ const exportDir = path.join(fbDir, 'training-data');
297
+
298
+ ensureDir(exportDir);
299
+
300
+ const entries = readJSONL(feedbackPath);
301
+ const sequences = readJSONL(sequencePath);
302
+
303
+ // Aggregate action patterns from sequences
304
+ const allPatterns = {};
305
+ for (const s of sequences) {
306
+ const patterns = (s.features && s.features.actionPatterns) || {};
307
+ for (const [tag, counts] of Object.entries(patterns)) {
308
+ if (!allPatterns[tag]) {
309
+ allPatterns[tag] = { positive: 0, negative: 0, total: 0 };
310
+ }
311
+ allPatterns[tag].positive += counts.positive || 0;
312
+ allPatterns[tag].negative += counts.negative || 0;
313
+ allPatterns[tag].total += (counts.positive || 0) + (counts.negative || 0);
314
+ }
315
+ }
316
+
317
+ // Compute success rates
318
+ for (const data of Object.values(allPatterns)) {
319
+ data.successRate = data.total > 0
320
+ ? +(data.positive / data.total).toFixed(4)
321
+ : null;
322
+ }
323
+
324
+ // Sort by total occurrences
325
+ const sortedPatterns = Object.entries(allPatterns)
326
+ .sort((a, b) => b[1].total - a[1].total)
327
+ .reduce((acc, [k, v]) => { acc[k] = v; return acc; }, {});
328
+
329
+ // Identify top failure modes (successRate < 0.4 with enough data)
330
+ const topFailureModes = Object.entries(allPatterns)
331
+ .filter(([, v]) => v.successRate !== null && v.successRate < 0.4 && v.total >= 2)
332
+ .sort((a, b) => a[1].successRate - b[1].successRate)
333
+ .slice(0, 5)
334
+ .map(([tag, v]) => ({
335
+ action: tag,
336
+ successRate: v.successRate,
337
+ total: v.total,
338
+ failureCount: v.negative,
339
+ }));
340
+
341
+ // Summary stats from feedback log
342
+ const posCount = entries.filter((e) => e.signal === 'positive' || e.feedback === 'up').length;
343
+ const negCount = entries.filter((e) => e.signal === 'negative' || e.feedback === 'down').length;
344
+
345
+ const report = {
346
+ generatedAt: new Date().toISOString(),
347
+ summary: {
348
+ totalFeedbackEntries: entries.length,
349
+ positiveEntries: posCount,
350
+ negativeEntries: negCount,
351
+ totalSequences: sequences.length,
352
+ uniqueActions: Object.keys(sortedPatterns).length,
353
+ },
354
+ actionPatterns: sortedPatterns,
355
+ topFailureModes,
356
+ recommendations: generateActionRecommendations(sortedPatterns),
357
+ };
358
+
359
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
360
+ const outPath = outputPath || path.join(exportDir, `action-analysis-${timestamp}.json`);
361
+ fs.writeFileSync(outPath, JSON.stringify(report, null, 2));
362
+
363
+ return { outputPath: outPath, report };
364
+ }
365
+
366
+ /**
367
+ * Generate recommendations from action pattern data.
368
+ *
369
+ * @param {object} patterns
370
+ * @returns {string[]}
371
+ */
372
+ function generateActionRecommendations(patterns) {
373
+ const recs = [];
374
+ for (const [tag, data] of Object.entries(patterns)) {
375
+ if (data.successRate !== null && data.total >= 3) {
376
+ if (data.successRate < 0.5) {
377
+ recs.push(`Avoid "${tag}" — ${(data.successRate * 100).toFixed(0)}% success rate across ${data.total} uses.`);
378
+ } else if (data.successRate > 0.8) {
379
+ recs.push(`Expand "${tag}" — ${(data.successRate * 100).toFixed(0)}% success rate across ${data.total} uses.`);
380
+ }
381
+ }
382
+ }
383
+ if (recs.length === 0) recs.push('No actionable recommendations at this time.');
384
+ return recs;
385
+ }
386
+
387
+ // ---------------------------------------------------------------------------
388
+ // CLI
389
+ // ---------------------------------------------------------------------------
390
+
391
+ function printUsage() {
392
+ console.log(`
393
+ Training Data Exporter — Phase 10 (XPRT-01..04)
394
+
395
+ Usage:
396
+ node export-training.js --pytorch [--output <path>]
397
+ node export-training.js --csv [--output <path>]
398
+ node export-training.js --actions [--output <path>]
399
+ node export-training.js --all
400
+
401
+ Options:
402
+ --pytorch Export PyTorch JSON format (XPRT-01)
403
+ --csv Export CSV summary (XPRT-02)
404
+ --actions Export action analysis report (XPRT-03)
405
+ --all Export all formats
406
+ --output Override output file path
407
+ --feedback-dir Override feedback directory
408
+ `);
409
+ }
410
+
411
+ if (require.main === module) {
412
+ const args = process.argv.slice(2).reduce((acc, arg) => {
413
+ if (arg.startsWith('--')) {
414
+ const [k, ...v] = arg.slice(2).split('=');
415
+ acc[k] = v.length ? v.join('=') : true;
416
+ }
417
+ return acc;
418
+ }, {});
419
+
420
+ if (args.help || Object.keys(args).length === 0) {
421
+ printUsage();
422
+ process.exit(0);
423
+ }
424
+
425
+ const fbDir = args['feedback-dir'] || undefined;
426
+ const outPath = args.output || undefined;
427
+
428
+ if (args.pytorch || args.all) {
429
+ const { outputPath, pairCount, sequenceCount } = exportPyTorchJSON(fbDir, outPath);
430
+ console.log(`PyTorch JSON: ${outputPath} (${pairCount} pairs, ${sequenceCount} sequences)`);
431
+ }
432
+ if (args.csv || args.all) {
433
+ const { outputPath, rowCount } = exportCSV(fbDir, outPath);
434
+ console.log(`CSV: ${outputPath} (${rowCount} rows)`);
435
+ }
436
+ if (args.actions || args.all) {
437
+ const { outputPath } = exportActionAnalysis(fbDir, outPath);
438
+ console.log(`Action Analysis: ${outputPath}`);
439
+ }
440
+ }
441
+
442
+ module.exports = {
443
+ exportPyTorchJSON,
444
+ exportCSV,
445
+ exportActionAnalysis,
446
+ buildPreferencePairs,
447
+ validateMemoryStructure,
448
+ escapeCsvField,
449
+ generateActionRecommendations,
450
+ };