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,145 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ const PROJECT_ROOT = path.join(__dirname, '..');
9
+ const HOME = process.env.HOME || process.env.USERPROFILE || os.homedir() || '';
10
+
11
+ function buildFeedbackPathsFromDir(dirPath) {
12
+ return {
13
+ FEEDBACK_DIR: dirPath,
14
+ FEEDBACK_LOG_PATH: path.join(dirPath, 'feedback-log.jsonl'),
15
+ DIAGNOSTIC_LOG_PATH: path.join(dirPath, 'diagnostic-log.jsonl'),
16
+ MEMORY_LOG_PATH: path.join(dirPath, 'memory-log.jsonl'),
17
+ REJECTION_LEDGER_PATH: path.join(dirPath, 'rejection-ledger.jsonl'),
18
+ SUMMARY_PATH: path.join(dirPath, 'feedback-summary.json'),
19
+ PREVENTION_RULES_PATH: path.join(dirPath, 'prevention-rules.md'),
20
+ };
21
+ }
22
+
23
+ function uniquePaths(paths = []) {
24
+ const seen = new Set();
25
+ const unique = [];
26
+
27
+ for (const candidate of paths) {
28
+ if (!candidate) continue;
29
+ const resolved = path.resolve(candidate);
30
+ if (seen.has(resolved)) continue;
31
+ seen.add(resolved);
32
+ unique.push(candidate);
33
+ }
34
+
35
+ return unique;
36
+ }
37
+
38
+ function dirExists(dirPath) {
39
+ try {
40
+ return Boolean(dirPath && fs.existsSync(dirPath));
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
46
+ function getProjectName(cwd = process.cwd()) {
47
+ return path.basename(cwd || PROJECT_ROOT) || 'default';
48
+ }
49
+
50
+ function getExplicitFeedbackDir(options = {}) {
51
+ const env = options.env || process.env;
52
+ if (options.feedbackDir) return options.feedbackDir;
53
+ if (env.THUMBGATE_FEEDBACK_DIR) return env.THUMBGATE_FEEDBACK_DIR;
54
+ if (env.RAILWAY_VOLUME_MOUNT_PATH) {
55
+ return path.join(env.RAILWAY_VOLUME_MOUNT_PATH, 'feedback');
56
+ }
57
+ return null;
58
+ }
59
+
60
+ function getThumbgateFeedbackDir(options = {}) {
61
+ const cwd = options.cwd || process.cwd();
62
+ return path.join(cwd, '.thumbgate');
63
+ }
64
+
65
+ function getRlhfFeedbackDir(options = {}) {
66
+ const env = options.env || process.env;
67
+ if (env._TEST_RLHF_FEEDBACK_DIR) return env._TEST_RLHF_FEEDBACK_DIR;
68
+ if (env.THUMBGATE_RLHF_FEEDBACK_DIR) return env.THUMBGATE_RLHF_FEEDBACK_DIR;
69
+ const cwd = options.cwd || process.cwd();
70
+ return path.join(cwd, '.rlhf');
71
+ }
72
+
73
+ function getLegacyFeedbackDir(options = {}) {
74
+ const env = options.env || process.env;
75
+ if (env._TEST_LEGACY_FEEDBACK_DIR) return env._TEST_LEGACY_FEEDBACK_DIR;
76
+ if (env.THUMBGATE_LEGACY_FEEDBACK_DIR) return env.THUMBGATE_LEGACY_FEEDBACK_DIR;
77
+ const cwd = options.cwd || process.cwd();
78
+ return path.join(cwd, '.claude', 'memory', 'feedback');
79
+ }
80
+
81
+ function getGlobalFeedbackDir(options = {}) {
82
+ const cwd = options.cwd || process.cwd();
83
+ const home = options.home || HOME;
84
+ return path.join(home, '.thumbgate', 'projects', getProjectName(cwd));
85
+ }
86
+
87
+ function resolveFeedbackDir(options = {}) {
88
+ const explicit = getExplicitFeedbackDir(options);
89
+ if (explicit) return explicit;
90
+
91
+ const localThumbgate = getThumbgateFeedbackDir(options);
92
+ if (dirExists(localThumbgate)) return localThumbgate;
93
+
94
+ const localRlhf = getRlhfFeedbackDir(options);
95
+ if (dirExists(localRlhf)) return localRlhf;
96
+
97
+ const localLegacy = getLegacyFeedbackDir(options);
98
+ if (dirExists(localLegacy)) return localLegacy;
99
+
100
+ return getGlobalFeedbackDir(options);
101
+ }
102
+
103
+ function getFeedbackPaths(options = {}) {
104
+ return buildFeedbackPathsFromDir(resolveFeedbackDir(options));
105
+ }
106
+
107
+ function listFallbackFeedbackDirs(options = {}) {
108
+ const activeDir = path.resolve(resolveFeedbackDir(options));
109
+ return uniquePaths([
110
+ getRlhfFeedbackDir(options),
111
+ getLegacyFeedbackDir(options),
112
+ ]).filter((dirPath) => path.resolve(dirPath) !== activeDir);
113
+ }
114
+
115
+ function listFeedbackArtifactPaths(fileName, options = {}) {
116
+ if (!fileName) return [];
117
+ const activeDir = resolveFeedbackDir(options);
118
+ return uniquePaths([
119
+ path.join(activeDir, fileName),
120
+ ...listFallbackFeedbackDirs(options).map((dirPath) => path.join(dirPath, fileName)),
121
+ ]);
122
+ }
123
+
124
+ function resolveFallbackArtifactPath(fileName, options = {}) {
125
+ const fallbackPaths = listFallbackFeedbackDirs(options).map((dirPath) => path.join(dirPath, fileName));
126
+ for (const candidate of fallbackPaths) {
127
+ if (fs.existsSync(candidate)) return candidate;
128
+ }
129
+ return fallbackPaths[0] || null;
130
+ }
131
+
132
+ module.exports = {
133
+ PROJECT_ROOT,
134
+ HOME,
135
+ buildFeedbackPathsFromDir,
136
+ getFeedbackPaths,
137
+ getGlobalFeedbackDir,
138
+ getLegacyFeedbackDir,
139
+ getRlhfFeedbackDir,
140
+ getThumbgateFeedbackDir,
141
+ listFallbackFeedbackDirs,
142
+ listFeedbackArtifactPaths,
143
+ resolveFallbackArtifactPath,
144
+ resolveFeedbackDir,
145
+ };
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+
3
+ const GENERIC_PHRASE_RULES = {
4
+ positive: [
5
+ /^up$/,
6
+ /^thumbs?\s*up$/,
7
+ /^thumbs\s+up$/,
8
+ /^that worked$/,
9
+ /^it worked$/,
10
+ /^worked$/,
11
+ /^looks good$/,
12
+ /^looked good$/,
13
+ /^good job$/,
14
+ /^good work$/,
15
+ /^nice work$/,
16
+ /^perfect$/,
17
+ /^approved$/,
18
+ /^lgtm$/,
19
+ ],
20
+ negative: [
21
+ /^down$/,
22
+ /^thumbs?\s*down$/,
23
+ /^thumbs\s+down$/,
24
+ /^that failed$/,
25
+ /^it failed$/,
26
+ /^failed$/,
27
+ /^that was wrong$/,
28
+ /^wrong$/,
29
+ /^bad$/,
30
+ /^fix this$/,
31
+ /^broken$/,
32
+ ],
33
+ };
34
+
35
+ const CLARIFICATION_CONFIG = {
36
+ positive: {
37
+ prompt: 'What specifically worked that should be repeated?',
38
+ example: 'Example: "The agent showed test output before claiming done."',
39
+ missingFields: ['whatWorked'],
40
+ },
41
+ negative: {
42
+ prompt: 'What failed and what should change next time?',
43
+ example: 'Example: "It skipped tests and should run npm test before closing the task."',
44
+ missingFields: ['whatWentWrong', 'whatToChange'],
45
+ },
46
+ };
47
+
48
+ function normalizeFeedbackSignal(signal) {
49
+ const normalized = normalizeFeedbackText(signal);
50
+ if (['negative', 'down', 'thumbs down', 'thumbsdown', 'bad'].includes(normalized)) {
51
+ return 'negative';
52
+ }
53
+ return 'positive';
54
+ }
55
+
56
+ function normalizeFeedbackText(value) {
57
+ return String(value || '')
58
+ .toLowerCase()
59
+ .replace(/[_-]+/g, ' ')
60
+ .replace(/[^\w\s]/g, ' ')
61
+ .replace(/\s+/g, ' ')
62
+ .trim();
63
+ }
64
+
65
+ function isGenericFeedbackText(value, signal) {
66
+ const normalized = normalizeFeedbackText(value);
67
+ if (!normalized) return false;
68
+ const rules = GENERIC_PHRASE_RULES[signal] || [];
69
+ return rules.some((pattern) => pattern.test(normalized));
70
+ }
71
+
72
+ function assessFeedbackActionability(params = {}) {
73
+ const signal = normalizeFeedbackSignal(params.signal);
74
+ const primaryFields = signal === 'positive'
75
+ ? [
76
+ { name: 'whatWorked', value: params.whatWorked },
77
+ { name: 'context', value: params.context },
78
+ ]
79
+ : [
80
+ { name: 'whatWentWrong', value: params.whatWentWrong },
81
+ { name: 'context', value: params.context },
82
+ ];
83
+
84
+ const populated = primaryFields.filter((field) => normalizeFeedbackText(field.value));
85
+ const specific = populated.find((field) => !isGenericFeedbackText(field.value, signal));
86
+
87
+ if (specific) {
88
+ return {
89
+ promotable: true,
90
+ signal,
91
+ sourceField: specific.name,
92
+ prompt: null,
93
+ example: null,
94
+ missingFields: [],
95
+ issue: null,
96
+ isGenericContext: false,
97
+ };
98
+ }
99
+
100
+ const config = CLARIFICATION_CONFIG[signal];
101
+ const issue = populated.length > 0 ? 'generic' : 'missing';
102
+
103
+ return {
104
+ promotable: false,
105
+ signal,
106
+ sourceField: null,
107
+ prompt: config.prompt,
108
+ example: config.example,
109
+ missingFields: config.missingFields,
110
+ issue,
111
+ isGenericContext: populated.some((field) => field.name === 'context'),
112
+ };
113
+ }
114
+
115
+ function buildClarificationMessage(params = {}) {
116
+ const assessment = assessFeedbackActionability(params);
117
+ if (assessment.promotable) return null;
118
+
119
+ const intro = assessment.signal === 'positive'
120
+ ? 'Positive signal logged, but it is not specific enough to promote to reusable memory.'
121
+ : 'Negative signal logged, but it is not specific enough to promote to reusable memory.';
122
+
123
+ return {
124
+ needsClarification: true,
125
+ prompt: assessment.prompt,
126
+ example: assessment.example,
127
+ missingFields: assessment.missingFields,
128
+ message: `${intro} ${assessment.prompt}`,
129
+ };
130
+ }
131
+
132
+ module.exports = {
133
+ GENERIC_PHRASE_RULES,
134
+ normalizeFeedbackSignal,
135
+ normalizeFeedbackText,
136
+ isGenericFeedbackText,
137
+ assessFeedbackActionability,
138
+ buildClarificationMessage,
139
+ };
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const {
7
+ getThumbgateFeedbackDir,
8
+ listFeedbackArtifactPaths,
9
+ } = require('./feedback-paths');
10
+
11
+ const JSONL_ARTIFACTS = new Set([
12
+ 'funnel-events.jsonl',
13
+ 'telemetry-pings.jsonl',
14
+ 'revenue-events.jsonl',
15
+ 'workflow-sprint-leads.jsonl',
16
+ ]);
17
+
18
+ const JSON_ARTIFACTS = new Map([
19
+ ['api-keys.json', () => ({ keys: {} })],
20
+ ['local-checkout-sessions.json', () => ({ sessions: {} })],
21
+ ]);
22
+
23
+ const CONSOLIDATED_ARTIFACTS = [
24
+ 'api-keys.json',
25
+ 'funnel-events.jsonl',
26
+ 'telemetry-pings.jsonl',
27
+ 'revenue-events.jsonl',
28
+ 'local-checkout-sessions.json',
29
+ 'workflow-sprint-leads.jsonl',
30
+ ];
31
+
32
+ function ensureParentDir(filePath) {
33
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
34
+ }
35
+
36
+ function readJsonFile(filePath, fallbackFactory) {
37
+ if (!filePath || !fs.existsSync(filePath)) return fallbackFactory();
38
+ try {
39
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
40
+ return parsed && typeof parsed === 'object' ? parsed : fallbackFactory();
41
+ } catch {
42
+ return fallbackFactory();
43
+ }
44
+ }
45
+
46
+ function readJsonlFile(filePath) {
47
+ if (!filePath || !fs.existsSync(filePath)) return [];
48
+ const raw = fs.readFileSync(filePath, 'utf8').trim();
49
+ if (!raw) return [];
50
+ return raw
51
+ .split('\n')
52
+ .map((line) => {
53
+ try {
54
+ return JSON.parse(line);
55
+ } catch {
56
+ return null;
57
+ }
58
+ })
59
+ .filter(Boolean);
60
+ }
61
+
62
+ function stableArtifactTimestamp(record = {}) {
63
+ return String(
64
+ record.timestamp ||
65
+ record.receivedAt ||
66
+ record.submittedAt ||
67
+ record.updatedAt ||
68
+ record.createdAt ||
69
+ ''
70
+ );
71
+ }
72
+
73
+ function dedupeJsonlRows(rows = []) {
74
+ const merged = [];
75
+ const seen = new Set();
76
+
77
+ for (const row of rows) {
78
+ if (!row || typeof row !== 'object') continue;
79
+ const key = JSON.stringify(row);
80
+ if (seen.has(key)) continue;
81
+ seen.add(key);
82
+ merged.push(row);
83
+ }
84
+
85
+ return merged.sort((a, b) => {
86
+ const timeCompare = stableArtifactTimestamp(a).localeCompare(stableArtifactTimestamp(b));
87
+ if (timeCompare !== 0) return timeCompare;
88
+ return JSON.stringify(a).localeCompare(JSON.stringify(b));
89
+ });
90
+ }
91
+
92
+ function mergeKeyStorePayloads(payloads = []) {
93
+ const merged = { keys: {} };
94
+ for (const payload of payloads) {
95
+ if (!payload || typeof payload !== 'object') continue;
96
+ Object.assign(merged, payload);
97
+ merged.keys = {
98
+ ...(merged.keys || {}),
99
+ ...((payload && payload.keys) || {}),
100
+ };
101
+ }
102
+ return merged;
103
+ }
104
+
105
+ function mergeCheckoutSessionsPayloads(payloads = []) {
106
+ const merged = { sessions: {} };
107
+ for (const payload of payloads) {
108
+ if (!payload || typeof payload !== 'object') continue;
109
+ Object.assign(merged, payload);
110
+ merged.sessions = {
111
+ ...(merged.sessions || {}),
112
+ ...((payload && payload.sessions) || {}),
113
+ };
114
+ }
115
+ return merged;
116
+ }
117
+
118
+ function serializeJson(payload) {
119
+ return `${JSON.stringify(payload, null, 2)}\n`;
120
+ }
121
+
122
+ function serializeJsonl(rows = []) {
123
+ const serialized = rows.map((row) => JSON.stringify(row)).join('\n');
124
+ return serialized ? `${serialized}\n` : '';
125
+ }
126
+
127
+ function writeIfChanged(filePath, nextContent, write = true) {
128
+ const currentContent = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
129
+ const changed = currentContent !== nextContent;
130
+
131
+ if (write && changed) {
132
+ ensureParentDir(filePath);
133
+ fs.writeFileSync(filePath, nextContent, 'utf8');
134
+ }
135
+
136
+ return {
137
+ changed,
138
+ wrote: write && changed,
139
+ };
140
+ }
141
+
142
+ function consolidateJsonArtifact(fileName, primaryPath, sourcePaths, write) {
143
+ const fallbackFactory = JSON_ARTIFACTS.get(fileName);
144
+ const payloads = sourcePaths.map((candidate) => readJsonFile(candidate, fallbackFactory));
145
+ const merged = fileName === 'api-keys.json'
146
+ ? mergeKeyStorePayloads(payloads)
147
+ : mergeCheckoutSessionsPayloads(payloads);
148
+ const initializedEmpty = sourcePaths.length === 0;
149
+ const writeResult = writeIfChanged(primaryPath, serializeJson(merged), write);
150
+
151
+ return {
152
+ fileName,
153
+ format: 'json',
154
+ primaryPath,
155
+ sourcePaths,
156
+ sourceCount: sourcePaths.length,
157
+ initializedEmpty,
158
+ wrote: writeResult.wrote,
159
+ changed: writeResult.changed,
160
+ keyCount: Object.keys(merged.keys || {}).length,
161
+ sessionCount: Object.keys(merged.sessions || {}).length,
162
+ };
163
+ }
164
+
165
+ function consolidateJsonlArtifact(fileName, primaryPath, sourcePaths, write) {
166
+ const merged = dedupeJsonlRows(sourcePaths.flatMap((candidate) => readJsonlFile(candidate)));
167
+ const initializedEmpty = sourcePaths.length === 0;
168
+ const writeResult = writeIfChanged(primaryPath, serializeJsonl(merged), write);
169
+
170
+ return {
171
+ fileName,
172
+ format: 'jsonl',
173
+ primaryPath,
174
+ sourcePaths,
175
+ sourceCount: sourcePaths.length,
176
+ initializedEmpty,
177
+ wrote: writeResult.wrote,
178
+ changed: writeResult.changed,
179
+ rowCount: merged.length,
180
+ };
181
+ }
182
+
183
+ function consolidateArtifact(fileName, options = {}) {
184
+ const artifactPaths = listFeedbackArtifactPaths(fileName, options);
185
+ const primaryPath = artifactPaths[0];
186
+ const sourcePaths = artifactPaths.filter((candidate) => fs.existsSync(candidate));
187
+ const write = options.write !== false;
188
+
189
+ if (JSONL_ARTIFACTS.has(fileName)) {
190
+ return consolidateJsonlArtifact(fileName, primaryPath, sourcePaths, write);
191
+ }
192
+
193
+ return consolidateJsonArtifact(fileName, primaryPath, sourcePaths, write);
194
+ }
195
+
196
+ function consolidateFeedbackRoot(options = {}) {
197
+ const feedbackDir = options.feedbackDir
198
+ || process.env.THUMBGATE_FEEDBACK_DIR
199
+ || getThumbgateFeedbackDir(options);
200
+ const artifacts = CONSOLIDATED_ARTIFACTS.map((fileName) => consolidateArtifact(fileName, {
201
+ ...options,
202
+ feedbackDir,
203
+ }));
204
+ const sourceRoots = Array.from(new Set(
205
+ artifacts.flatMap((artifact) => artifact.sourcePaths.map((candidate) => path.dirname(candidate)))
206
+ )).sort();
207
+
208
+ return {
209
+ feedbackDir,
210
+ write: options.write !== false,
211
+ artifactCount: artifacts.length,
212
+ wroteArtifacts: artifacts.filter((artifact) => artifact.wrote).length,
213
+ changedArtifacts: artifacts.filter((artifact) => artifact.changed).length,
214
+ initializedArtifacts: artifacts.filter((artifact) => artifact.initializedEmpty).map((artifact) => artifact.fileName),
215
+ sourceRoots,
216
+ artifacts,
217
+ };
218
+ }
219
+
220
+ function runCli() {
221
+ const args = new Set(process.argv.slice(2));
222
+ const dryRun = args.has('--dry-run');
223
+ const summary = consolidateFeedbackRoot({ write: !dryRun });
224
+ process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
225
+ }
226
+
227
+ module.exports = {
228
+ CONSOLIDATED_ARTIFACTS,
229
+ consolidateArtifact,
230
+ consolidateFeedbackRoot,
231
+ dedupeJsonlRows,
232
+ mergeCheckoutSessionsPayloads,
233
+ mergeKeyStorePayloads,
234
+ };
235
+
236
+ if (require.main === module) {
237
+ runCli();
238
+ }