rlhf-feedback-loop 0.6.6 → 0.6.8

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.
package/README.md CHANGED
@@ -1,30 +1,39 @@
1
- # RLHF Feedback Loop
1
+ # Agentic Feedback Studio — The Veto Layer & RLHF-Ready Dataset Engine
2
2
 
3
3
  [![CI](https://github.com/IgorGanapolsky/rlhf-feedback-loop/actions/workflows/ci.yml/badge.svg)](https://github.com/IgorGanapolsky/rlhf-feedback-loop/actions/workflows/ci.yml)
4
- [![npm](https://img.shields.io/npm/v/rlhf-feedback-loop)](https://www.npmjs.com/package/rlhf-feedback-loop)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
6
- [![MCP Ready](https://img.shields.io/badge/MCP-ready-black)](adapters/mcp/server-stdio.js)
7
- [![DPO Ready](https://img.shields.io/badge/DPO-ready-blue)](scripts/export-dpo-pairs.js)
4
+ [![Marketplace Ready](https://img.shields.io/badge/Anthropic_Marketplace-Ready-blue)](docs/ANTHROPIC_MARKETPLACE_STRATEGY.md)
5
+ [![Veto Powered](https://img.shields.io/badge/Governance-Veto_Layer-red)](docs/VERIFICATION_EVIDENCE.md)
8
6
 
9
- **The complete RLHF data pipeline for AI coding agents.** Capture human feedback, build memory, generate prevention rules, and export DPO training pairs the full loop from thumbs up/down to model fine-tuning.
7
+ **The operational layer for high-density preference data.** Stop vibe-coding and start context engineering. The Agentic Feedback Studio provides the **Veto Layer** for AI workflows, capturing human feedback to generate **RLHF-ready datasets** and enforce kernel-level guardrails.
10
8
 
11
- ## What This Is (and Isn't)
9
+ ## Why This Matters: From Vibes to Verification (V2V)
12
10
 
13
- This tool implements the **data collection and preference pipeline** side of RLHF — the part that turns your daily interactions with AI agents into structured training data. Out of the box, it:
11
+ Most AI agents run on "vibes." We provide the infrastructure to convert those vibes into **Hard Evidence** for continuous improvement.
14
12
 
15
- - **Captures** thumbs up/down feedback with context, tags, and rubric scores
16
- - **Remembers** via JSONL logs + LanceDB vector search across sessions
17
- - **Prevents** repeated mistakes with auto-generated guardrails
18
- - **Recalls** relevant past feedback mid-conversation (in-session context injection)
19
- - **Exports** DPO training pairs (prompt/chosen/rejected) for model fine-tuning
13
+ - **Veto Layer (Governance):** Convert subjective user feedback into non-bypassable architectural constraints (`CLAUDE.md`).
14
+ - **RLHF-Ready Datasets:** Automatically generate high-density DPO (Direct Preference Optimization) pairs from real-world agent interactions.
15
+ - **Online Bayesian Reward Estimation:** Uses Thompson Sampling to model user preferences in real-time, providing a local "Reward Signal" without heavy training.
20
16
 
21
- It does **not** update model weights in real-time. That's the fine-tuning step, which you do separately using the DPO pairs this tool exports. The full loop: capture feedback here → export DPO pairs → fine-tune with [TRL](https://github.com/huggingface/trl), [OpenPipe](https://openpipe.ai), or any DPO trainer → deploy improved model.
17
+ ## True Plug-and-Play: Zero-Config Integration
22
18
 
23
- ## Architecture
19
+ The Feedback Studio is a **Universal Agent Skill**. You can drop it into any repository without manual setup.
24
20
 
25
- ![RLHF Architecture](docs/diagrams/rlhf-architecture-pb.png)
21
+ - **Zero-Config Discovery:** Automatically detects project context. If no local `.rlhf/` directory exists, it safely fallbacks to a project-scoped global store in `~/.rlhf/`.
22
+ - **Global Skill Installation:** Run one command to make the Studio available to all your agents across all projects.
26
23
 
27
- ![Plugin Topology](docs/diagrams/plugin-topology-pb.png)
24
+ ### Quick Start (One Command)
25
+
26
+ ```bash
27
+ npx rlhf-feedback-loop install
28
+ ```
29
+
30
+ This will auto-detect your platforms (Claude, Codex, Gemini, Cursor) and install the feedback skill globally.
31
+
32
+ ## Use Cases
33
+
34
+ - **Automated Code Reviews:** Capture PR feedback to enforce team-specific style guides autonomously.
35
+ - **Self-Healing Multi-Agent Systems:** Share Veto rules across a swarm of agents to avoid systemic bottlenecks.
36
+ - **DPO Dataset Engineering:** Collect proprietary preference data to fine-tune smaller, faster models that perform like GPT-4 on your specific codebase.
28
37
 
29
38
  ## Get Started
30
39
 
@@ -37,14 +46,11 @@ One command. Pick your platform:
37
46
  | **Gemini** | `gemini mcp add rlhf "npx -y rlhf-feedback-loop serve"` |
38
47
  | **Amp** | `amp mcp add rlhf -- npx -y rlhf-feedback-loop serve` |
39
48
  | **Cursor** | `cursor mcp add rlhf -- npx -y rlhf-feedback-loop serve` |
40
- | **All at once** | `npx add-mcp rlhf-feedback-loop` |
41
-
42
- That's it. Your agent can now capture feedback, recall past learnings mid-conversation, and block repeated mistakes. Run once per project — the MCP server starts automatically on each session.
43
49
 
44
50
  ## How It Works
45
51
 
46
52
  ```
47
- Thumbs up/down
53
+ Subjective Signal (Vibe)
48
54
  |
49
55
  v
50
56
  Capture → JSONL log
@@ -57,14 +63,14 @@ Thumbs up/down
57
63
  Good Bad
58
64
  | |
59
65
  v v
60
- Learn Prevention rule
66
+ Learn Veto Layer (Rule)
61
67
  | |
62
68
  v v
63
69
  LanceDB ShieldCortex
64
70
  vectors context packs
65
71
  |
66
72
  v
67
- DPO export → fine-tune your model
73
+ DPO export → RLHF / Fine-tune your model
68
74
  ```
69
75
 
70
76
  All data stored locally as **JSONL** files — fully transparent, fully portable, no vendor lock-in. **LanceDB** indexes memories as vector embeddings for semantic search. **ShieldCortex** assembles context packs so your agent starts each task informed.
@@ -80,18 +86,9 @@ The open-source package is fully functional and free forever. Cloud Pro is for t
80
86
  | DPO export | CLI command | API endpoint |
81
87
  | Setup | `mcp add` one-liner | Provisioned API key |
82
88
  | Team sharing | Manual (share JSONL) | Built-in (shared API) |
83
- | Support | GitHub Issues | Email |
84
- | Uptime | You manage | We manage (99.9% SLA) |
85
89
 
86
90
  [Get Cloud Pro](https://buy.stripe.com/bJe14neyU4r4f0leOD3sI02) | [Live API](https://rlhf-feedback-loop-710216278770.us-central1.run.app)
87
91
 
88
- ## Deep Dive
89
-
90
- - [API Reference](openapi/openapi.yaml) — full OpenAPI spec
91
- - [Context Engine](docs/CONTEXTFS.md) — multi-agent memory orchestration
92
- - [Autonomous GitOps](docs/AUTONOMOUS_GITOPS.md) — self-healing CI/CD
93
- - [Contributing](CONTRIBUTING.md)
94
-
95
92
  ## License
96
93
 
97
94
  MIT. See [LICENSE](LICENSE).
package/bin/cli.js CHANGED
@@ -6,11 +6,8 @@
6
6
  * npx rlhf-feedback-loop init # scaffold .rlhf/ config + .mcp.json
7
7
  * npx rlhf-feedback-loop capture # capture feedback
8
8
  * npx rlhf-feedback-loop export-dpo # export DPO training pairs
9
- * npx rlhf-feedback-loop stats # feedback analytics
10
- * npx rlhf-feedback-loop rules # generate prevention rules
11
- * npx rlhf-feedback-loop self-heal # run self-healing check + fix
12
- * npx rlhf-feedback-loop prove # run proof harness
13
- * npx rlhf-feedback-loop start-api # start HTTPS API server
9
+ * npx rlhf-feedback-loop stats # feedback analytics + Revenue-at-Risk
10
+ * npx rlhf-feedback-loop pro # upgrade to Cloud Pro
14
11
  */
15
12
 
16
13
  'use strict';
@@ -201,7 +198,7 @@ function capture() {
201
198
  const { captureFeedback, analyzeFeedback, feedbackSummary, writePreventionRules } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
202
199
 
203
200
  if (args.stats) {
204
- console.log(JSON.stringify(analyzeFeedback(), null, 2));
201
+ stats();
205
202
  return;
206
203
  }
207
204
 
@@ -248,7 +245,42 @@ function capture() {
248
245
 
249
246
  function stats() {
250
247
  const { analyzeFeedback } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
251
- console.log(JSON.stringify(analyzeFeedback(), null, 2));
248
+ const data = analyzeFeedback();
249
+
250
+ console.log('\n📊 RLHF Performance Metrics');
251
+ console.log('─'.repeat(50));
252
+ console.log(` Total Signals : ${data.total}`);
253
+ console.log(` Approval Rate : ${Math.round(data.approvalRate * 100)}%`);
254
+ console.log(` Recent Trend : ${Math.round(data.recentRate * 100)}%`);
255
+
256
+ // The Pitch: Revenue-at-Risk
257
+ const avgCostOfMistake = 2.50; // $2.50 per agent turn/fix
258
+ const revenueAtRisk = (data.totalNegative * avgCostOfMistake).toFixed(2);
259
+
260
+ if (data.totalNegative > 0) {
261
+ console.log('\n⚠️ REVENUE-AT-RISK ANALYSIS');
262
+ console.log(` Repeated Failures detected: ${data.totalNegative}`);
263
+ console.log(` Estimated Operational Loss: $${revenueAtRisk}`);
264
+ console.log(' Action Required: Run "npx rlhf-feedback-loop rules" to generate guardrails.');
265
+ console.log(' Strategic Recommendation: Upgrade to Cloud Pro to sync these rules across your team.');
266
+ console.log(' Run: npx rlhf-feedback-loop pro');
267
+ } else {
268
+ console.log('\n✅ System is currently high-reliability. No immediate revenue loss detected.');
269
+ }
270
+ }
271
+
272
+ function pro() {
273
+ const stripeUrl = 'https://buy.stripe.com/bJe14neyU4r4f0leOD3sI02';
274
+ console.log('\n🚀 RLHF Feedback Loop — Cloud Pro');
275
+ console.log('─'.repeat(50));
276
+ console.log('Unlock the full Agentic Control Plane:');
277
+ console.log(' - Hosted Team API (Shared memory across all repos)');
278
+ console.log(' - ShieldCortex Managed Context Packs');
279
+ console.log(' - Automated DPO Training Pipelines');
280
+ console.log(' - SOC2-ready Governance Dashboard');
281
+ console.log('\n👉 Complete your upgrade here:');
282
+ console.log(` ${stripeUrl}`);
283
+ console.log('\nOnce upgraded, run: npx rlhf-feedback-loop init --key=YOUR_PRO_KEY\n');
252
284
  }
253
285
 
254
286
  function summary() {
@@ -307,11 +339,38 @@ function prove() {
307
339
  }
308
340
 
309
341
  function serve() {
310
- // Start MCP server over stdio — used by `claude mcp add`, `codex mcp add`, `gemini mcp add`
342
+ const rlhfDir = path.join(CWD, '.rlhf');
343
+ if (!fs.existsSync(rlhfDir) && !fs.existsSync(path.join(CWD, '.claude', 'memory', 'feedback'))) {
344
+ // If not initialized, ensure global fallback exists
345
+ const projectName = path.basename(CWD) || 'default';
346
+ const globalDir = path.join(HOME, '.rlhf', 'projects', projectName);
347
+ if (!fs.existsSync(globalDir)) {
348
+ fs.mkdirSync(globalDir, { recursive: true });
349
+ }
350
+ }
351
+
352
+ // Start MCP server over stdio
311
353
  const mcpServer = path.join(PKG_ROOT, 'adapters', 'mcp', 'server-stdio.js');
312
354
  require(mcpServer);
313
355
  }
314
356
 
357
+ function install() {
358
+ console.log('Installing RLHF Feedback Loop as a global MCP skill...');
359
+ const results = [
360
+ setupClaude(),
361
+ setupCodex(),
362
+ setupGemini(),
363
+ setupCursor()
364
+ ];
365
+ const success = results.some(r => r === true);
366
+ if (success) {
367
+ console.log('\nSuccess! RLHF Feedback Loop is now available to your agents.');
368
+ console.log('Try asking your agent: "Capture positive feedback for this task"');
369
+ } else {
370
+ console.log('\nRLHF Feedback Loop is already configured.');
371
+ }
372
+ }
373
+
315
374
  function startApi() {
316
375
  const serverPath = path.join(PKG_ROOT, 'src', 'api', 'server.js');
317
376
  try {
@@ -329,32 +388,29 @@ function help() {
329
388
  console.log(' init Scaffold .rlhf/ config + MCP server in current project');
330
389
  console.log(' serve Start MCP server (stdio) — for claude/codex/gemini mcp add');
331
390
  console.log(' capture [flags] Capture feedback (--feedback=up|down --context="..." --tags="...")');
332
- console.log(' stats Show feedback analytics');
391
+ console.log(' stats Show feedback analytics + Revenue-at-Risk');
333
392
  console.log(' summary Human-readable feedback summary');
334
393
  console.log(' export-dpo Export DPO training pairs (prompt/chosen/rejected JSONL)');
335
394
  console.log(' rules Generate prevention rules from repeated failures');
336
395
  console.log(' self-heal Run self-healing check and auto-fix');
396
+ console.log(' pro Upgrade to Cloud Pro ($10/mo)');
337
397
  console.log(' prove [--target=X] Run proof harness (adapters|automation|attribution|lancedb|...)');
338
398
  console.log(' start-api Start the RLHF HTTPS API server');
339
399
  console.log(' help Show this help message');
340
400
  console.log('');
341
401
  console.log('Examples:');
342
402
  console.log(' npx rlhf-feedback-loop init');
343
- console.log(' npx rlhf-feedback-loop capture --feedback=up --context="all tests pass"');
344
- console.log(' npx rlhf-feedback-loop capture --feedback=down --context="broke prod" --what-went-wrong="no tests"');
345
- console.log(' npx rlhf-feedback-loop export-dpo');
346
403
  console.log(' npx rlhf-feedback-loop stats');
347
- console.log('');
348
- console.log('MCP install (one command per platform):');
349
- console.log(' claude mcp add rlhf -- npx -y rlhf-feedback-loop serve');
350
- console.log(' codex mcp add rlhf -- npx -y rlhf-feedback-loop serve');
351
- console.log(' gemini mcp add rlhf -- npx -y rlhf-feedback-loop serve');
404
+ console.log(' npx rlhf-feedback-loop pro');
352
405
  }
353
406
 
354
407
  switch (COMMAND) {
355
408
  case 'init':
356
409
  init();
357
410
  break;
411
+ case 'install':
412
+ install();
413
+ break;
358
414
  case 'serve':
359
415
  case 'mcp':
360
416
  serve();
@@ -379,6 +435,9 @@ switch (COMMAND) {
379
435
  case 'self-heal':
380
436
  selfHeal();
381
437
  break;
438
+ case 'pro':
439
+ pro();
440
+ break;
382
441
  case 'prove':
383
442
  prove();
384
443
  break;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rlhf-feedback-loop",
3
- "version": "0.6.6",
4
- "description": "Make your AI agent learn from mistakes. Capture thumbs up/down feedback, block repeated failures, export DPO training data. Works with ChatGPT, Claude, Codex, Gemini, Amp.",
3
+ "version": "0.6.8",
4
+ "description": "Production RLHF & DPO data pipeline for AI agents. Optimize agentic reliability with Feedback-Driven Development (FDD). Capture human preference signals, generate automated guardrails, and export DPO training pairs. Compatible with Claude, GPT-4, Gemini, and multi-agent systems.",
5
5
  "homepage": "https://github.com/IgorGanapolsky/rlhf-feedback-loop#readme",
6
6
  "repository": {
7
7
  "type": "git",
@@ -91,6 +91,13 @@
91
91
  "amp",
92
92
  "mcp",
93
93
  "model-context-protocol",
94
+ "agentic-reliability",
95
+ "feedback-driven-development",
96
+ "agentic-workflows",
97
+ "ai-search-optimization",
98
+ "generative-engine-optimization",
99
+ "fdd",
100
+ "geo",
94
101
  "agent-evaluation",
95
102
  "prompt-engineering",
96
103
  "context-engineering",
@@ -101,10 +108,12 @@
101
108
  ],
102
109
  "license": "MIT",
103
110
  "dependencies": {
104
- "@huggingface/transformers": "^3.8.1",
105
- "@lancedb/lancedb": "^0.26.2",
106
111
  "apache-arrow": "^18.1.0",
107
112
  "stripe": "^20.4.0"
108
113
  },
109
- "mcpName": "io.github.IgorGanapolsky/rlhf-feedback-loop"
114
+ "mcpName": "io.github.IgorGanapolsky/rlhf-feedback-loop",
115
+ "optionalDependencies": {
116
+ "@lancedb/lancedb": "^0.26.2",
117
+ "@huggingface/transformers": "^3.8.1"
118
+ }
110
119
  }
@@ -25,8 +25,9 @@ const crypto = require('crypto');
25
25
 
26
26
  const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY || '';
27
27
  const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET || '';
28
+ const GITHUB_MARKETPLACE_WEBHOOK_SECRET = process.env.GITHUB_MARKETPLACE_WEBHOOK_SECRET || '';
28
29
  const STRIPE_PRICE_ID = process.env.STRIPE_PRICE_ID || 'price_cloud_pro_49_monthly';
29
- const API_KEYS_PATH = path.resolve(
30
+ const API_KEYS_PATH = process.env._TEST_API_KEYS_PATH || path.resolve(
30
31
  __dirname,
31
32
  '../.claude/memory/feedback/api-keys.json'
32
33
  );
@@ -443,20 +444,106 @@ function verifyWebhookSignature(rawBody, signature) {
443
444
 
444
445
  // Constant-time comparison
445
446
  try {
446
- return crypto.timingSafeEqual(
447
- Buffer.from(expected, 'hex'),
448
- Buffer.from(parts.v1, 'hex')
449
- );
447
+ return crypto.timingSafeEqual(
448
+ Buffer.from(expected, 'hex'),
449
+ Buffer.from(parts.v1, 'hex')
450
+ );
450
451
  } catch {
451
- return false;
452
+ return false;
453
+ }
452
454
  }
453
- }
454
455
 
455
- // ---------------------------------------------------------------------------
456
- // Module exports
457
- // ---------------------------------------------------------------------------
456
+ /**
457
+ * Verify a GitHub Marketplace webhook signature.
458
+ * Returns true if valid, false if GITHUB_MARKETPLACE_WEBHOOK_SECRET is not set (local mode).
459
+ *
460
+ * @param {string|Buffer} rawBody - Raw request body bytes
461
+ * @param {string} signature - Value of x-hub-signature-256 header
462
+ * @returns {boolean}
463
+ */
464
+ function verifyGithubWebhookSignature(rawBody, signature) {
465
+ if (!GITHUB_MARKETPLACE_WEBHOOK_SECRET) {
466
+ // Local mode — skip signature verification
467
+ return true;
468
+ }
469
+
470
+ if (!signature || !rawBody) {
471
+ return false;
472
+ }
473
+
474
+ const hmac = crypto.createHmac('sha256', GITHUB_MARKETPLACE_WEBHOOK_SECRET);
475
+ const digest = Buffer.from('sha256=' + hmac.update(rawBody).digest('hex'), 'utf8');
476
+ const checksum = Buffer.from(signature, 'utf8');
477
+
478
+ // Constant-time comparison
479
+ return checksum.length === digest.length && crypto.timingSafeEqual(digest, checksum);
480
+ }
481
+
482
+ /**
483
+ * Handle a GitHub Marketplace webhook event.
484
+ *
485
+ * Supported actions:
486
+ * purchased — provision API key for the new customer
487
+ * changed — plan update (upgrade/downgrade)
488
+ * cancelled — disable all keys for that customer
489
+ *
490
+ * @param {object} event - Parsed GitHub Marketplace event object
491
+ * @returns {{ handled: boolean, action?: string, result?: object }}
492
+ */
493
+ function handleGithubWebhook(event) {
494
+ const { action, marketplace_purchase } = event;
495
+ if (!action || !marketplace_purchase) {
496
+ return { handled: false, reason: 'missing_payload_data' };
497
+ }
498
+
499
+ const account = marketplace_purchase.account;
500
+ if (!account || !account.id) {
501
+ return { handled: false, reason: 'missing_account_id' };
502
+ }
503
+
504
+ // Map GitHub account to customerId: github_<user|organization>_<id>
505
+ const customerId = `github_${account.type.toLowerCase()}_${account.id}`;
458
506
 
459
- module.exports = {
507
+ switch (action) {
508
+ case 'purchased': {
509
+ const result = provisionApiKey(customerId);
510
+ return {
511
+ handled: true,
512
+ action: 'provisioned_api_key',
513
+ result,
514
+ };
515
+ }
516
+
517
+ case 'cancelled': {
518
+ const result = disableCustomerKeys(customerId);
519
+ return {
520
+ handled: true,
521
+ action: 'disabled_customer_keys',
522
+ result,
523
+ };
524
+ }
525
+
526
+ case 'changed': {
527
+ // In this simple model, we just ensure a key exists and is active.
528
+ // Upgrades/downgrades don't change basic API access unless we had tiered features.
529
+ const result = provisionApiKey(customerId);
530
+ return {
531
+ handled: true,
532
+ action: 'plan_changed',
533
+ result,
534
+ };
535
+ }
536
+
537
+ default:
538
+ return { handled: false, reason: `unhandled_action:${action}` };
539
+ }
540
+ }
541
+
542
+ // ---------------------------------------------------------------------------
543
+ // Module exports
544
+ // ---------------------------------------------------------------------------
545
+
546
+ module.exports = {
460
547
  createCheckoutSession,
461
548
  provisionApiKey,
462
549
  validateApiKey,
@@ -464,8 +551,11 @@ module.exports = {
464
551
  disableCustomerKeys,
465
552
  handleWebhook,
466
553
  verifyWebhookSignature,
554
+ verifyGithubWebhookSignature,
555
+ handleGithubWebhook,
467
556
  loadKeyStore,
468
557
  // Expose for testing
469
558
  _API_KEYS_PATH: API_KEYS_PATH,
470
559
  _LOCAL_MODE: () => LOCAL_MODE,
471
- };
560
+ };
561
+
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # GSD: Deploy RLHF Control Plane to Google Cloud Run
3
+
4
+ PROJECT_ID=$(gcloud config get-value project)
5
+ SERVICE_NAME="rlhf-control-plane"
6
+ REGION="us-central1"
7
+
8
+ echo "🚀 Deploying Agentic Control Plane to $REGION..."
9
+
10
+ gcloud builds submit --tag gcr.io/$PROJECT_ID/$SERVICE_NAME
11
+ gcloud run deploy $SERVICE_NAME \
12
+ --image gcr.io/$PROJECT_ID/$SERVICE_NAME \
13
+ --platform managed \
14
+ --region $REGION \
15
+ --allow-unauthenticated \
16
+ --set-env-vars RLHF_ALLOW_INSECURE=true
17
+
18
+ echo "✅ Success! Your Control Plane is live."
19
+ gcloud run services describe $SERVICE_NAME --region $REGION --format='value(status.url)'
@@ -29,14 +29,43 @@ const DOMAIN_CATEGORIES = [
29
29
  'git-workflow', 'documentation', 'debugging', 'architecture', 'data-modeling',
30
30
  ];
31
31
 
32
+ const HOME = process.env.HOME || process.env.USERPROFILE || '';
33
+
32
34
  function getFeedbackPaths() {
33
- const feedbackDir = process.env.RLHF_FEEDBACK_DIR || DEFAULT_FEEDBACK_DIR;
35
+ if (process.env.RLHF_FEEDBACK_DIR) {
36
+ const d = process.env.RLHF_FEEDBACK_DIR;
37
+ return {
38
+ FEEDBACK_DIR: d,
39
+ FEEDBACK_LOG_PATH: path.join(d, 'feedback-log.jsonl'),
40
+ MEMORY_LOG_PATH: path.join(d, 'memory-log.jsonl'),
41
+ SUMMARY_PATH: path.join(d, 'feedback-summary.json'),
42
+ PREVENTION_RULES_PATH: path.join(d, 'prevention-rules.md'),
43
+ };
44
+ }
45
+
46
+ // Auto-discovery order:
47
+ // 1. .rlhf/ (Standard)
48
+ // 2. .claude/memory/feedback/ (Legacy Claude)
49
+ // 3. ~/.rlhf/projects/<cwd-basename>/ (Global fallback for true plug-and-play)
50
+
51
+ const localRlhf = path.join(process.cwd(), '.rlhf');
52
+ const localClaude = path.join(process.cwd(), '.claude', 'memory', 'feedback');
53
+
54
+ let baseDir = localRlhf;
55
+ if (!fs.existsSync(localRlhf) && fs.existsSync(localClaude)) {
56
+ baseDir = localClaude;
57
+ } else if (!fs.existsSync(localRlhf)) {
58
+ // Zero-Config Global Fallback
59
+ const projectName = path.basename(process.cwd()) || 'default';
60
+ baseDir = path.join(HOME, '.rlhf', 'projects', projectName);
61
+ }
62
+
34
63
  return {
35
- FEEDBACK_DIR: feedbackDir,
36
- FEEDBACK_LOG_PATH: path.join(feedbackDir, 'feedback-log.jsonl'),
37
- MEMORY_LOG_PATH: path.join(feedbackDir, 'memory-log.jsonl'),
38
- SUMMARY_PATH: path.join(feedbackDir, 'feedback-summary.json'),
39
- PREVENTION_RULES_PATH: path.join(feedbackDir, 'prevention-rules.md'),
64
+ FEEDBACK_DIR: baseDir,
65
+ FEEDBACK_LOG_PATH: path.join(baseDir, 'feedback-log.jsonl'),
66
+ MEMORY_LOG_PATH: path.join(baseDir, 'memory-log.jsonl'),
67
+ SUMMARY_PATH: path.join(baseDir, 'feedback-summary.json'),
68
+ PREVENTION_RULES_PATH: path.join(baseDir, 'prevention-rules.md'),
40
69
  };
41
70
  }
42
71
 
@@ -323,8 +323,10 @@ async function runProof(options = {}) {
323
323
  } catch (err) {
324
324
  addResult('fatal', false, { error: err.message });
325
325
  } finally {
326
- await new Promise((resolve) => server.close(resolve));
327
- fs.rmSync(tmpFeedbackDir, { recursive: true, force: true });
326
+ if (server) await new Promise((resolve) => server.close(resolve));
327
+ try {
328
+ fs.rmSync(tmpFeedbackDir, { recursive: true, force: true });
329
+ } catch (e) {}
328
330
  }
329
331
 
330
332
  if (writeArtifacts) {
@@ -140,8 +140,8 @@ async function runProof() {
140
140
  let vec03Status = 'fail';
141
141
  let vec03Evidence = '';
142
142
  try {
143
- const arrowSpec = PKG.dependencies['apache-arrow'] || '';
144
- const lanceSpec = PKG.dependencies['@lancedb/lancedb'] || '';
143
+ const arrowSpec = PKG.dependencies['apache-arrow'] || PKG.optionalDependencies['apache-arrow'] || '';
144
+ const lanceSpec = PKG.dependencies['@lancedb/lancedb'] || PKG.optionalDependencies['@lancedb/lancedb'] || '';
145
145
 
146
146
  // Check if spec pins to <= 18.1.0 (either "18.1.0", "^18.1.0", or "~18.1.0")
147
147
  const arrowVersion = arrowSpec.replace(/[\^~>=<]*/g, '').split('.').map(Number);
package/src/api/server.js CHANGED
@@ -37,6 +37,8 @@ const {
37
37
  recordUsage,
38
38
  handleWebhook,
39
39
  verifyWebhookSignature,
40
+ verifyGithubWebhookSignature,
41
+ handleGithubWebhook,
40
42
  loadKeyStore,
41
43
  } = require('../../scripts/billing');
42
44
 
@@ -195,6 +197,30 @@ function createApiServer() {
195
197
  return;
196
198
  }
197
199
 
200
+ if (req.method === 'GET' && pathname === '/.well-known/mcp/server-card.json') {
201
+ sendJson(res, 200, {
202
+ name: 'rlhf-feedback-loop',
203
+ description: 'RLHF feedback loop for AI agents. Capture feedback, block mistakes, export DPO data.',
204
+ version: pkg.version,
205
+ tools: [
206
+ { name: 'recall', description: 'Recall relevant past feedback for current task' },
207
+ { name: 'capture_feedback', description: 'Capture thumbs up/down feedback' },
208
+ { name: 'feedback_stats', description: 'Feedback analytics' },
209
+ { name: 'feedback_summary', description: 'Human-readable feedback summary' },
210
+ { name: 'prevention_rules', description: 'Generate prevention rules from failures' },
211
+ { name: 'export_dpo_pairs', description: 'Export DPO training pairs' },
212
+ { name: 'construct_context_pack', description: 'Build bounded context pack' },
213
+ { name: 'evaluate_context_pack', description: 'Record context pack outcome' },
214
+ { name: 'context_provenance', description: 'Audit trail of context decisions' },
215
+ { name: 'list_intents', description: 'Available action plans' },
216
+ { name: 'plan_intent', description: 'Generate execution plan' },
217
+ ],
218
+ repository: 'https://github.com/IgorGanapolsky/rlhf-feedback-loop',
219
+ homepage: 'https://rlhf-feedback-loop-710216278770.us-central1.run.app',
220
+ });
221
+ return;
222
+ }
223
+
198
224
  if (req.method === 'GET' && pathname === '/health') {
199
225
  sendJson(res, 200, {
200
226
  status: 'ok',
@@ -240,6 +266,42 @@ function createApiServer() {
240
266
  return;
241
267
  }
242
268
 
269
+ // GitHub Marketplace webhook
270
+ if (req.method === 'POST' && pathname === '/v1/billing/github-webhook') {
271
+ try {
272
+ const rawBody = await new Promise((resolve, reject) => {
273
+ const chunks = [];
274
+ req.on('data', (c) => chunks.push(c));
275
+ req.on('end', () => resolve(Buffer.concat(chunks)));
276
+ req.on('error', reject);
277
+ });
278
+
279
+ const sig = req.headers['x-hub-signature-256'] || '';
280
+ if (!verifyGithubWebhookSignature(rawBody, sig)) {
281
+ sendJson(res, 400, { error: 'Invalid webhook signature' });
282
+ return;
283
+ }
284
+
285
+ let event;
286
+ try {
287
+ event = JSON.parse(rawBody.toString('utf-8'));
288
+ } catch {
289
+ sendJson(res, 400, { error: 'Invalid JSON in webhook body' });
290
+ return;
291
+ }
292
+
293
+ const result = handleGithubWebhook(event);
294
+ sendJson(res, 200, result);
295
+ } catch (err) {
296
+ if (err.statusCode) {
297
+ sendJson(res, err.statusCode, { error: err.message });
298
+ } else {
299
+ sendJson(res, 500, { error: err.message || 'Internal Server Error' });
300
+ }
301
+ }
302
+ return;
303
+ }
304
+
243
305
  if (!isAuthorized(req, expectedApiKey)) {
244
306
  sendJson(res, 401, { error: 'Unauthorized' });
245
307
  return;