thumbgate 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/.claude-plugin/marketplace.json +32 -13
  2. package/.claude-plugin/plugin.json +15 -2
  3. package/.well-known/llms.txt +60 -0
  4. package/.well-known/mcp/server-card.json +1 -1
  5. package/README.md +109 -20
  6. package/adapters/README.md +1 -1
  7. package/adapters/chatgpt/openapi.yaml +168 -0
  8. package/adapters/claude/.mcp.json +2 -2
  9. package/adapters/codex/config.toml +2 -2
  10. package/adapters/mcp/server-stdio.js +84 -1
  11. package/adapters/opencode/opencode.json +1 -1
  12. package/bin/cli.js +200 -13
  13. package/bin/postinstall.js +8 -2
  14. package/config/budget.json +18 -0
  15. package/config/gates/code-edit.json +61 -0
  16. package/config/gates/db-write.json +61 -0
  17. package/config/gates/default.json +154 -3
  18. package/config/gates/deploy.json +61 -0
  19. package/config/github-about.json +2 -1
  20. package/config/merge-quality-checks.json +23 -0
  21. package/openapi/openapi.yaml +168 -0
  22. package/package.json +42 -10
  23. package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
  24. package/plugins/claude-codex-bridge/.mcp.json +1 -1
  25. package/plugins/claude-codex-bridge/scripts/codex-bridge.js +1 -3
  26. package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
  27. package/plugins/codex-profile/.mcp.json +1 -1
  28. package/plugins/codex-profile/INSTALL.md +27 -4
  29. package/plugins/codex-profile/README.md +33 -9
  30. package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
  31. package/plugins/opencode-profile/INSTALL.md +1 -1
  32. package/public/blog.html +73 -0
  33. package/public/compare/mem0.html +189 -0
  34. package/public/compare/speclock.html +180 -0
  35. package/public/compare.html +10 -2
  36. package/public/guide.html +2 -2
  37. package/public/guides/claude-code-prevent-repeated-mistakes.html +161 -0
  38. package/public/guides/codex-cli-guardrails.html +158 -0
  39. package/public/guides/cursor-prevent-repeated-mistakes.html +161 -0
  40. package/public/guides/pre-action-gates.html +162 -0
  41. package/public/guides/stop-repeated-ai-agent-mistakes.html +159 -0
  42. package/public/index.html +136 -50
  43. package/public/lessons.html +33 -24
  44. package/public/llm-context.md +140 -0
  45. package/public/pro.html +24 -22
  46. package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
  47. package/scripts/access-anomaly-detector.js +1 -1
  48. package/scripts/adk-consolidator.js +1 -5
  49. package/scripts/agent-security-hardening.js +4 -6
  50. package/scripts/agentic-data-pipeline.js +1 -3
  51. package/scripts/async-job-runner.js +1 -5
  52. package/scripts/audit-trail.js +1 -5
  53. package/scripts/background-agent-governance.js +2 -10
  54. package/scripts/billing.js +2 -16
  55. package/scripts/budget-enforcer.js +173 -0
  56. package/scripts/build-codex-plugin.js +152 -0
  57. package/scripts/check-congruence.js +132 -14
  58. package/scripts/commercial-offer.js +5 -7
  59. package/scripts/content-engine/linkedin-content-generator.js +154 -0
  60. package/scripts/content-engine/output/linkedin-memento-validation.md +17 -0
  61. package/scripts/content-engine/output/linkedin-posts-2026-04-09.md +175 -0
  62. package/scripts/content-engine/reddit-thread-finder.js +154 -0
  63. package/scripts/context-engine.js +21 -6
  64. package/scripts/contextfs.js +1 -21
  65. package/scripts/dashboard.js +20 -0
  66. package/scripts/decision-journal.js +341 -0
  67. package/scripts/delegation-runtime.js +1 -5
  68. package/scripts/distribution-surfaces.js +26 -0
  69. package/scripts/document-intake.js +927 -0
  70. package/scripts/ephemeral-agent-store.js +1 -8
  71. package/scripts/evolution-state.js +1 -5
  72. package/scripts/experiment-tracker.js +1 -5
  73. package/scripts/export-databricks-bundle.js +1 -5
  74. package/scripts/export-hf-dataset.js +1 -5
  75. package/scripts/export-training.js +1 -5
  76. package/scripts/feedback-attribution.js +1 -16
  77. package/scripts/feedback-history-distiller.js +1 -16
  78. package/scripts/feedback-loop.js +1 -5
  79. package/scripts/feedback-root-consolidator.js +2 -21
  80. package/scripts/feedback-session.js +49 -0
  81. package/scripts/feedback-to-rules.js +188 -28
  82. package/scripts/filesystem-search.js +1 -9
  83. package/scripts/fs-utils.js +104 -0
  84. package/scripts/gates-engine.js +149 -4
  85. package/scripts/github-about.js +32 -8
  86. package/scripts/gtm-revenue-loop.js +1 -5
  87. package/scripts/harness-selector.js +148 -0
  88. package/scripts/hosted-job-launcher.js +1 -5
  89. package/scripts/hybrid-feedback-context.js +7 -33
  90. package/scripts/intervention-policy.js +58 -1
  91. package/scripts/lesson-db.js +3 -18
  92. package/scripts/lesson-inference.js +194 -16
  93. package/scripts/lesson-retrieval.js +60 -24
  94. package/scripts/llm-client.js +59 -0
  95. package/scripts/managed-lesson-agent.js +183 -0
  96. package/scripts/marketing-experiment.js +8 -22
  97. package/scripts/meta-agent-loop.js +624 -0
  98. package/scripts/metered-billing.js +1 -1
  99. package/scripts/money-watcher.js +1 -4
  100. package/scripts/obsidian-export.js +1 -5
  101. package/scripts/operational-integrity.js +15 -3
  102. package/scripts/org-dashboard.js +6 -1
  103. package/scripts/per-step-scoring.js +2 -4
  104. package/scripts/pr-manager.js +201 -19
  105. package/scripts/pro-features.js +3 -2
  106. package/scripts/prompt-dlp.js +3 -3
  107. package/scripts/prove-adapters.js +1 -5
  108. package/scripts/prove-attribution.js +1 -5
  109. package/scripts/prove-automation.js +1 -3
  110. package/scripts/prove-cloudflare-sandbox.js +1 -3
  111. package/scripts/prove-data-pipeline.js +1 -3
  112. package/scripts/prove-intelligence.js +1 -3
  113. package/scripts/prove-lancedb.js +1 -5
  114. package/scripts/prove-local-intelligence.js +1 -3
  115. package/scripts/prove-packaged-runtime.js +75 -9
  116. package/scripts/prove-predictive-insights.js +1 -3
  117. package/scripts/prove-training-export.js +1 -3
  118. package/scripts/prove-workflow-contract.js +1 -5
  119. package/scripts/rate-limiter.js +3 -1
  120. package/scripts/reddit-dm-outreach.js +14 -4
  121. package/scripts/schedule-manager.js +3 -5
  122. package/scripts/security-scanner.js +448 -0
  123. package/scripts/self-distill-agent.js +579 -0
  124. package/scripts/semantic-dedup.js +115 -0
  125. package/scripts/skill-exporter.js +1 -3
  126. package/scripts/skill-generator.js +1 -5
  127. package/scripts/social-analytics/engagement-audit.js +1 -18
  128. package/scripts/social-analytics/pollers/linkedin.js +26 -16
  129. package/scripts/social-analytics/publishers/linkedin.js +1 -1
  130. package/scripts/social-analytics/publishers/zernio.js +51 -0
  131. package/scripts/social-pipeline.js +1 -3
  132. package/scripts/social-post-hourly.js +47 -4
  133. package/scripts/statusline-links.js +6 -5
  134. package/scripts/statusline.sh +29 -153
  135. package/scripts/sync-branch-protection.js +340 -0
  136. package/scripts/tessl-export.js +1 -3
  137. package/scripts/thumbgate-search.js +32 -1
  138. package/scripts/tool-kpi-tracker.js +1 -1
  139. package/scripts/tool-registry.js +106 -2
  140. package/scripts/vector-store.js +1 -5
  141. package/scripts/weekly-auto-post.js +1 -1
  142. package/scripts/workflow-sentinel.js +91 -0
  143. package/skills/thumbgate/SKILL.md +1 -1
  144. package/src/api/server.js +273 -4
  145. package/scripts/social-analytics/db/social-analytics.db-shm +0 -0
  146. /package/scripts/social-analytics/db/{social-analytics.db-wal → analytics.sqlite} +0 -0
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+ const { resolveFeedbackDir } = require('./feedback-paths');
8
+ const { parseFeedbackFile, classifySignal, analyzeWithLLM, analyze, promoteToGates } = require('./feedback-to-rules');
9
+ const { inferStructuredLessonLLM, inferStructuredLesson, createLesson } = require('./lesson-inference');
10
+ const { isAvailable } = require('./llm-client');
11
+
12
+ const MAX_ENTRIES_PER_RUN = 20;
13
+ const DELAY_BETWEEN_CALLS_MS = 500;
14
+ const MANIFEST_DIR = path.join(os.homedir(), '.thumbgate');
15
+ const MANIFEST_PATH = path.join(MANIFEST_DIR, 'managed-agent-runs.jsonl');
16
+
17
+ function sleep(ms) {
18
+ return new Promise((resolve) => setTimeout(resolve, ms));
19
+ }
20
+
21
+ function getProcessedIds() {
22
+ if (!fs.existsSync(MANIFEST_PATH)) return new Set();
23
+ const ids = new Set();
24
+ for (const line of fs.readFileSync(MANIFEST_PATH, 'utf8').split('\n')) {
25
+ const trimmed = line.trim();
26
+ if (!trimmed) continue;
27
+ try {
28
+ const run = JSON.parse(trimmed);
29
+ if (Array.isArray(run.processedIds)) {
30
+ for (const id of run.processedIds) ids.add(id);
31
+ }
32
+ } catch { /* skip */ }
33
+ }
34
+ return ids;
35
+ }
36
+
37
+ function writeManifest(manifest) {
38
+ fs.mkdirSync(MANIFEST_DIR, { recursive: true });
39
+ fs.appendFileSync(MANIFEST_PATH, JSON.stringify(manifest) + '\n');
40
+ }
41
+
42
+ function getManagedAgentStatus() {
43
+ if (!fs.existsSync(MANIFEST_PATH)) return null;
44
+ const lines = fs.readFileSync(MANIFEST_PATH, 'utf8').split('\n').filter(Boolean);
45
+ if (lines.length === 0) return null;
46
+ try {
47
+ const last = JSON.parse(lines[lines.length - 1]);
48
+ return {
49
+ lastRun: last.runAt,
50
+ entriesProcessed: last.entriesProcessed,
51
+ lessonsCreated: last.lessonsCreated,
52
+ gatesPromoted: last.gatesPromoted,
53
+ model: last.model,
54
+ durationMs: last.durationMs,
55
+ totalRuns: lines.length,
56
+ };
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ async function runManagedAgent({ dryRun = false, limit, model } = {}) {
63
+ const startTime = Date.now();
64
+ const feedbackDir = resolveFeedbackDir();
65
+ const logPath = path.join(feedbackDir, 'feedback-log.jsonl');
66
+ const entries = parseFeedbackFile(logPath);
67
+
68
+ if (entries.length === 0) {
69
+ return { entriesProcessed: 0, lessonsCreated: 0, gatesPromoted: 0, model: 'none', durationMs: 0, message: 'No feedback entries found' };
70
+ }
71
+
72
+ const processedIds = getProcessedIds();
73
+ const pending = entries
74
+ .filter((e) => {
75
+ const id = e.id || e.feedbackId || e.timestamp;
76
+ return id && !processedIds.has(id);
77
+ })
78
+ .slice(0, limit || MAX_ENTRIES_PER_RUN);
79
+
80
+ if (pending.length === 0) {
81
+ return { entriesProcessed: 0, lessonsCreated: 0, gatesPromoted: 0, model: 'none', durationMs: Date.now() - startTime, message: 'All entries already processed' };
82
+ }
83
+
84
+ const useLLM = isAvailable();
85
+ const modelUsed = useLLM ? 'claude-haiku-4-5' : 'heuristic';
86
+ let lessonsCreated = 0;
87
+ const newProcessedIds = [];
88
+
89
+ for (const entry of pending) {
90
+ const id = entry.id || entry.feedbackId || entry.timestamp;
91
+ const signal = classifySignal(entry);
92
+ if (!signal) {
93
+ newProcessedIds.push(id);
94
+ continue;
95
+ }
96
+
97
+ const window = Array.isArray(entry.conversationWindow) ? entry.conversationWindow : [];
98
+ const context = entry.context || '';
99
+
100
+ let structuredLesson = null;
101
+ if (useLLM) {
102
+ structuredLesson = await inferStructuredLessonLLM(window, signal, context);
103
+ if (structuredLesson && !dryRun) {
104
+ await sleep(DELAY_BETWEEN_CALLS_MS);
105
+ }
106
+ }
107
+
108
+ if (!structuredLesson) {
109
+ structuredLesson = inferStructuredLesson(window, signal, context);
110
+ }
111
+
112
+ if (!dryRun && structuredLesson) {
113
+ try {
114
+ createLesson({
115
+ feedbackId: id,
116
+ signal,
117
+ inferredLesson: structuredLesson.action?.description || '',
118
+ triggerMessage: structuredLesson.examples?.[0]?.assistantAction || '',
119
+ priorSummary: '',
120
+ confidence: structuredLesson.confidence || 0.5,
121
+ tags: structuredLesson.tags || entry.tags || [],
122
+ metadata: { ...structuredLesson.metadata, managedAgent: true, format: structuredLesson.format },
123
+ });
124
+ lessonsCreated++;
125
+ } catch { /* lesson creation is best-effort */ }
126
+ } else if (dryRun && structuredLesson) {
127
+ lessonsCreated++;
128
+ }
129
+
130
+ newProcessedIds.push(id);
131
+ }
132
+
133
+ // Rule generation pass
134
+ let gatesPromoted = 0;
135
+ if (useLLM) {
136
+ const llmIssues = await analyzeWithLLM(entries);
137
+ if (llmIssues && llmIssues.length > 0) {
138
+ if (!dryRun) {
139
+ promoteToGates(llmIssues);
140
+ }
141
+ gatesPromoted = llmIssues.filter((i) => i.severity === 'critical').length;
142
+ }
143
+ } else {
144
+ const report = analyze(entries);
145
+ gatesPromoted = report.recurringIssues.filter((i) => i.severity === 'critical').length;
146
+ }
147
+
148
+ const manifest = {
149
+ runAt: new Date().toISOString(),
150
+ entriesProcessed: pending.length,
151
+ lessonsCreated,
152
+ gatesPromoted,
153
+ model: modelUsed,
154
+ dryRun,
155
+ durationMs: Date.now() - startTime,
156
+ processedIds: newProcessedIds,
157
+ };
158
+
159
+ if (!dryRun) {
160
+ writeManifest(manifest);
161
+ }
162
+
163
+ return manifest;
164
+ }
165
+
166
+ if (require.main === module) {
167
+ const args = process.argv.slice(2);
168
+ const dryRun = args.includes('--dry-run');
169
+ const limitFlag = args.find((a) => a.startsWith('--limit'));
170
+ const limit = limitFlag ? Number(args[args.indexOf(limitFlag) + 1]) || MAX_ENTRIES_PER_RUN : undefined;
171
+
172
+ runManagedAgent({ dryRun, limit })
173
+ .then((result) => {
174
+ console.log(JSON.stringify(result, null, 2));
175
+ process.exit(0);
176
+ })
177
+ .catch((err) => {
178
+ console.error('Managed agent error:', err.message);
179
+ process.exit(1);
180
+ });
181
+ }
182
+
183
+ module.exports = { runManagedAgent, getManagedAgentStatus };
@@ -23,6 +23,7 @@ const path = require('path');
23
23
  const { getFeedbackPaths, readJSONL } = require('./feedback-loop');
24
24
  const { loadModel, saveModel, updateModel, samplePosteriors, getReliability } = require('./thompson-sampling');
25
25
  const { buildResearchBrief } = require('./hf-papers');
26
+ const { ensureDir, readJsonl } = require('./fs-utils');
26
27
 
27
28
  // ---------------------------------------------------------------------------
28
29
  // Constants
@@ -80,27 +81,12 @@ function getMarketingPaths() {
80
81
  };
81
82
  }
82
83
 
83
- function ensureDir(dirPath) {
84
- if (!fs.existsSync(dirPath)) {
85
- fs.mkdirSync(dirPath, { recursive: true });
86
- }
87
- }
88
84
 
89
85
  function appendJSONL(filePath, record) {
90
86
  ensureDir(path.dirname(filePath));
91
87
  fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`);
92
88
  }
93
89
 
94
- function readJSONLFile(filePath) {
95
- if (!fs.existsSync(filePath)) return [];
96
- const lines = fs.readFileSync(filePath, 'utf-8').trim().split('\n').filter(Boolean);
97
- const records = [];
98
- for (const line of lines) {
99
- try { records.push(JSON.parse(line)); } catch { /* skip bad lines */ }
100
- }
101
- return records;
102
- }
103
-
104
90
  // ---------------------------------------------------------------------------
105
91
  // Variant Generation
106
92
  // ---------------------------------------------------------------------------
@@ -300,7 +286,7 @@ function recordVariantMetrics(params) {
300
286
  }
301
287
 
302
288
  const paths = getMarketingPaths();
303
- const variants = readJSONLFile(paths.variantsPath);
289
+ const variants = readJsonl(paths.variantsPath);
304
290
  const variant = variants.find(v => v.id === params.variantId);
305
291
 
306
292
  if (!variant) {
@@ -333,7 +319,7 @@ function selectWinners(params) {
333
319
  }
334
320
 
335
321
  const paths = getMarketingPaths();
336
- const allVariants = readJSONLFile(paths.variantsPath);
322
+ const allVariants = readJsonl(paths.variantsPath);
337
323
 
338
324
  // Get latest state of each variant in this batch (last write wins)
339
325
  const batchMap = new Map();
@@ -479,7 +465,7 @@ function extractPattern(variant) {
479
465
  */
480
466
  function getWinningPatterns(channel, limit = 5) {
481
467
  const paths = getMarketingPaths();
482
- const winners = readJSONLFile(paths.winnersPath);
468
+ const winners = readJsonl(paths.winnersPath);
483
469
  return winners
484
470
  .filter(w => w.channel === channel)
485
471
  .slice(-limit)
@@ -496,7 +482,7 @@ function getWinningPatterns(channel, limit = 5) {
496
482
  */
497
483
  function getKnowledgeBase(opts = {}) {
498
484
  const paths = getMarketingPaths();
499
- let entries = readJSONLFile(paths.knowledgePath);
485
+ let entries = readJsonl(paths.knowledgePath);
500
486
  if (opts.channel) {
501
487
  entries = entries.filter(e => e.channel === opts.channel);
502
488
  }
@@ -509,9 +495,9 @@ function getKnowledgeBase(opts = {}) {
509
495
 
510
496
  function updateMarketingProgress() {
511
497
  const paths = getMarketingPaths();
512
- const experiments = readJSONLFile(paths.experimentsPath);
513
- const knowledge = readJSONLFile(paths.knowledgePath);
514
- const winners = readJSONLFile(paths.winnersPath);
498
+ const experiments = readJsonl(paths.experimentsPath);
499
+ const knowledge = readJsonl(paths.knowledgePath);
500
+ const winners = readJsonl(paths.winnersPath);
515
501
 
516
502
  const channelStats = {};
517
503
  for (const ch of MARKETING_CHANNELS) {