@snapback/cli 1.1.15 → 3.0.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![npm downloads](https://img.shields.io/npm/dm/@snapback/cli.svg)](https://www.npmjs.com/package/@snapback/cli)
7
7
  [![License](https://img.shields.io/npm/l/@snapback/cli.svg)](https://github.com/snapback-dev/snapback-cli/blob/main/LICENSE)
8
8
 
9
- AI coding assistants like **Cursor**, **GitHub Copilot**, and **Claude** are incredibleuntil they're not. One wrong autocomplete can cascade into hours of debugging. Git doesn't help because you haven't committed yet.
9
+ AI coding agents—Cursor, Claude Code, GitHub Copilot, Windsurf—are incredible until they're not. One hallucinated change cascades into hours of debugging. Git doesn't help because you haven't committed yet. And the faster you vibe-code, the faster mistakes compound.
10
10
 
11
11
  **SnapBack makes your codebase smarter every session.** Day 1: Detection. Day 30: Patterns. Day 90: Prediction.
12
12
 
@@ -261,14 +261,33 @@ snap tools repair
261
261
 
262
262
  ### Available MCP Tools
263
263
 
264
- Once configured, your AI assistant can use:
264
+ Once configured, your AI assistant uses the **V2 4-tool surface** — a session-oriented API designed for agentic coding loops:
265
265
 
266
- | Tool | Description |
267
- |------|-------------|
268
- | `snapback.get_context` | Understand your codebase |
269
- | `snapback.analyze_risk` | Assess change risks |
270
- | `snapback.create_checkpoint` | Create safety snapshots (Pro) |
271
- | `snapback.restore_checkpoint` | Recover from mistakes (Pro) |
266
+ | Tool | When to call | What it returns |
267
+ |------|-------------|----------------|
268
+ | `snap_begin` | Start of every task | Intelligence briefing: past learnings, active warnings, session lineage |
269
+ | `snap_pulse` | Mid-session check | Read-only vitals: pulse level, risk pressure, trajectory |
270
+ | `snap_learn` | Any discovery | Captures a pattern, gotcha, or decision for future sessions |
271
+ | `snap_end` | Task complete | Ceremony: outcome recorded, carry-forward context for next session |
272
+
273
+ **The agentic workflow** (runs automatically in your AI assistant):
274
+
275
+ ```
276
+ snap_begin({ task: "refactor auth module" })
277
+ → briefing with learnings, warnings, risk context
278
+
279
+ [... agent works ...]
280
+
281
+ snap_pulse() // optional mid-session check
282
+ → pulse: elevated, pressure: 42%, trajectory: stable
283
+
284
+ snap_learn({ insight: "always snapshot before token refresh logic" })
285
+
286
+ snap_end({ outcome: "completed", summary: "..." })
287
+ → ceremony: files changed, patterns captured, context for next session
288
+ ```
289
+
290
+ The full surface (check, advise, safe_to_write, refactoring intelligence, learning intelligence) is also available for advanced workflows — see [MCP documentation →](https://docs.snapback.dev/mcp).
272
291
 
273
292
  ---
274
293
 
@@ -331,10 +350,13 @@ Once configured, your AI assistant can use:
331
350
 
332
351
  | Command | Description |
333
352
  |---------|-------------|
334
- | `snap context [task]` | Get relevant patterns before starting work |
353
+ | `snap context [task]` | Pre-task briefing: relevant patterns, warnings, risk context (CLI equivalent of `snap_begin`) |
335
354
  | `snap validate <file>` | Run 7-layer validation pipeline |
336
355
  | `snap validate --all` | Validate all staged files |
337
356
  | `snap stats` | Show learning statistics |
357
+ | `snap metrics [file]` | File momentum scores — which files are highest-churn / highest-risk |
358
+ | `snap sync` | Collect signals and fit momentum scoring normalizers |
359
+ | `snap refresh [--since]` | Incremental signal update |
338
360
 
339
361
  ### Learning Commands
340
362
 
@@ -374,14 +396,30 @@ Once configured, your AI assistant can use:
374
396
  | `snap tools validate` | Validate MCP configurations |
375
397
  | `snap tools repair` | Repair broken MCP configurations |
376
398
 
377
- ### Daemon Commands
399
+ ### Daemon & Service Commands
378
400
 
379
401
  | Command | Description |
380
402
  |---------|-------------|
381
- | `snap daemon start` | Start SnapBack daemon |
403
+ | `snap daemon start [--detach]` | Start SnapBack daemon (CLI↔Extension coordination) |
382
404
  | `snap daemon stop` | Stop SnapBack daemon |
383
405
  | `snap daemon status` | Check daemon status |
384
406
  | `snap daemon restart` | Restart daemon |
407
+ | `snap daemon ping` | Verify daemon is responsive |
408
+ | `snap service start [--daemon]` | Start local service (shared state for multi-client) |
409
+ | `snap service stop` | Stop local service |
410
+ | `snap service status` | Check service status |
411
+ | `snap service logs [--follow]` | Stream service logs |
412
+ | `snap service install` | Register as system service |
413
+ | `snap service uninstall` | Unregister system service |
414
+
415
+ ### Workspace Baseline Commands
416
+
417
+ | Command | Description |
418
+ |---------|-------------|
419
+ | `snap baseline scan` | Scan workspace and build intelligence baseline |
420
+ | `snap baseline status` | Show baseline freshness and coverage |
421
+ | `snap baseline show` | Display current baseline data |
422
+ | `snap baseline invalidate` | Force baseline rebuild on next scan |
385
423
 
386
424
  ### Utility Commands
387
425
 
@@ -390,7 +428,7 @@ Once configured, your AI assistant can use:
390
428
  | `snap wizard` | Interactive first-run setup |
391
429
  | `snap doctor` | Diagnostics and health check |
392
430
  | `snap doctor --fix` | Auto-fix detected issues |
393
- | `snap upgrade` | Check for CLI updates |
431
+ | `snap upgrade [--check|--canary]` | Update CLI or check for new version |
394
432
  | `snap config list` | List configuration values |
395
433
  | `snap config get <key>` | Get a specific config value |
396
434
  | `snap config set <key> <value>` | Set a configuration value |
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node --no-warnings=ExperimentalWarning
2
- import { config, EntitlementsServiceImpl, getBaseUrl, ENABLE_ENHANCED_2FA, ENABLE_SSO, ENABLE_CAPTCHA, ENABLE_MULTI_SESSION, getOrganizationWithPurchasesAndMembersCount, getPendingInvitationByEmail } from './chunk-23G5VYA3.js';
2
+ import { config, EntitlementsServiceImpl, getBaseUrl, ENABLE_ENHANCED_2FA, ENABLE_SSO, ENABLE_CAPTCHA, ENABLE_MULTI_SESSION, getOrganizationWithPurchasesAndMembersCount, getPendingInvitationByEmail } from './chunk-CPZWXRP2.js';
3
3
  import './chunk-GQ73B37K.js';
4
- import { combinedSchema, db } from './chunk-HR34NJP7.js';
5
- import { trackEvent } from './chunk-XYU5FFE3.js';
6
- import './chunk-ICKSHS3A.js';
4
+ import { combinedSchema, db } from './chunk-LIBBDBW5.js';
5
+ import { trackEvent } from './chunk-FMWCFAY7.js';
6
+ import './chunk-Q4VC7GND.js';
7
7
  import { logger } from './chunk-PL4HF4M2.js';
8
8
  import { createLogger, LogLevel } from './chunk-WS36HDEU.js';
9
9
  import './chunk-5EOPYJ4Y.js';
@@ -1043,7 +1043,7 @@ var _auth = betterAuth({
1043
1043
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1044
1044
  });
1045
1045
  try {
1046
- const { db: db2 } = await import('./dist-YZBJAYEJ.js');
1046
+ const { db: db2 } = await import('./dist-OO5LJHL6.js');
1047
1047
  const { sql } = await import('drizzle-orm');
1048
1048
  if (db2) {
1049
1049
  const result = await db2.execute(sql`
@@ -1073,7 +1073,7 @@ var _auth = betterAuth({
1073
1073
  }
1074
1074
  try {
1075
1075
  if (!session.activeOrganizationId) {
1076
- const { db: db2, combinedSchema: combinedSchema2 } = await import('./dist-YZBJAYEJ.js');
1076
+ const { db: db2, combinedSchema: combinedSchema2 } = await import('./dist-OO5LJHL6.js');
1077
1077
  const { sql } = await import('drizzle-orm');
1078
1078
  if (db2) {
1079
1079
  const { member: member2, session: sessionTable } = combinedSchema2;
@@ -1130,7 +1130,7 @@ var _auth = betterAuth({
1130
1130
  });
1131
1131
  }
1132
1132
  try {
1133
- const { autoProvisionOrganization } = await import('./auto-provision-organization-SF6XM7X4.js');
1133
+ const { autoProvisionOrganization } = await import('./auto-provision-organization-CXHL46P3.js');
1134
1134
  const result = await autoProvisionOrganization(user2);
1135
1135
  if (!result.success) {
1136
1136
  logger.warn("Auto-org provisioning failed", {
@@ -1424,7 +1424,7 @@ This invitation expires in ${expiresInDays} days.`,
1424
1424
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1425
1425
  }).catch(() => {
1426
1426
  });
1427
- import('./dist-E7E2T3DQ.js').then(({ captureError }) => {
1427
+ import('./dist-NFU5UJEW.js').then(({ captureError }) => {
1428
1428
  if (captureError && error instanceof Error) {
1429
1429
  captureError(error, {
1430
1430
  tags: {
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node --no-warnings=ExperimentalWarning
2
- import { trackEvent } from './chunk-XYU5FFE3.js';
3
- import './chunk-ICKSHS3A.js';
2
+ import { trackEvent } from './chunk-FMWCFAY7.js';
3
+ import './chunk-Q4VC7GND.js';
4
4
  import { logger } from './chunk-PL4HF4M2.js';
5
5
  import './chunk-WS36HDEU.js';
6
6
  import './chunk-5EOPYJ4Y.js';
@@ -56,7 +56,7 @@ __name(getDisplayName, "getDisplayName");
56
56
  async function autoProvisionOrganization(user) {
57
57
  const startTime = Date.now();
58
58
  try {
59
- const { db, combinedSchema } = await import('./dist-YZBJAYEJ.js');
59
+ const { db, combinedSchema } = await import('./dist-OO5LJHL6.js');
60
60
  const { sql } = await import('drizzle-orm');
61
61
  if (!db) {
62
62
  logger.error("Database not available for auto-provisioning", {
@@ -138,7 +138,7 @@ async function autoProvisionOrganization(user) {
138
138
  __name(autoProvisionOrganization, "autoProvisionOrganization");
139
139
  async function userHasOrganization(userId) {
140
140
  try {
141
- const { db, combinedSchema } = await import('./dist-YZBJAYEJ.js');
141
+ const { db, combinedSchema } = await import('./dist-OO5LJHL6.js');
142
142
  const { sql } = await import('drizzle-orm');
143
143
  if (!db) {
144
144
  return false;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node --no-warnings=ExperimentalWarning
2
2
  import { isRedisAvailable, getCache, setCache, deleteCache } from './chunk-GQ73B37K.js';
3
- import { snapshots, snapshotFiles, agentSuggestions, quarantineEvents, postAcceptOutcomes, policyEvaluations, loops, feedback, combinedSchema, user, aiChat, organization, member, invitation, purchase, session, account, verification, passkey, db, userAttributions, subscriptions, trials, pioneers, usageLimits, creditsLedger, mcpObservations, mcpToolInvocations, extensionSyncState, pioneerActions, sagas, apiKeys, TIER_STALENESS_THRESHOLD_MS, WORKSPACE_LINK_TTL_MS, patterns, closeDatabaseConnection, checkDatabaseConnection } from './chunk-HR34NJP7.js';
3
+ import { snapshots, snapshotFiles, agentSuggestions, quarantineEvents, postAcceptOutcomes, policyEvaluations, loops, feedback, telemetryEvents, telemetryIdempotencyKeys, combinedSchema, user, aiChat, organization, member, invitation, purchase, session, account, verification, passkey, db, userAttributions, subscriptions, trials, pioneers, usageLimits, creditsLedger, mcpObservations, mcpToolInvocations, extensionSyncState, pioneerActions, sagas, apiKeys, TIER_STALENESS_THRESHOLD_MS, WORKSPACE_LINK_TTL_MS, patterns, closeDatabaseConnection, checkDatabaseConnection } from './chunk-LIBBDBW5.js';
4
4
  import { logger } from './chunk-PL4HF4M2.js';
5
5
  import { shouldMergeAttribution, createLogger, LogLevel, isFeatureAvailableAtTier, getTierFeatures, getTierLimit, EventBus, generateIdempotencyKey, getActionPoints, calculatePioneerTier, PIONEER_TIER_THRESHOLDS, isFirstTimeAction, TIER_UPGRADE_SAGA } from './chunk-WS36HDEU.js';
6
6
  import { __name } from './chunk-7ADPL4Q3.js';
@@ -521,6 +521,179 @@ var TelemetrySinkDb = class {
521
521
  }
522
522
  }
523
523
  };
524
+ var TelemetrySinkDbAdapter = class {
525
+ static {
526
+ __name(this, "TelemetrySinkDbAdapter");
527
+ }
528
+ db;
529
+ constructor(db2) {
530
+ this.db = db2;
531
+ }
532
+ /**
533
+ * Store telemetry events with idempotency check
534
+ */
535
+ async storeEvents(events) {
536
+ if (events.length === 0) {
537
+ return;
538
+ }
539
+ try {
540
+ const values = events.map((event) => ({
541
+ id: event.id,
542
+ userId: this.extractUserId(event),
543
+ apiKeyId: this.extractApiKeyId(event),
544
+ eventType: event.eventType,
545
+ eventCategory: this.categorizeEvent(event),
546
+ properties: this.redactProperties(event.payload),
547
+ sessionId: event.context?.sessionId,
548
+ platform: event.context?.client,
549
+ timestamp: new Date(event.timestamp),
550
+ createdAt: /* @__PURE__ */ new Date()
551
+ }));
552
+ await this.db.insert(telemetryEvents).values(values);
553
+ } catch (error) {
554
+ console.error("Failed to store telemetry events", {
555
+ count: events.length,
556
+ error: error instanceof Error ? error.message : String(error)
557
+ });
558
+ throw error;
559
+ }
560
+ }
561
+ /**
562
+ * Retrieve telemetry events with optional filtering
563
+ */
564
+ async getEvents(filter) {
565
+ try {
566
+ const results = await this.db.select().from(telemetryEvents);
567
+ let events = results.map((row) => this.toTelemetryEvent(row));
568
+ if (filter?.eventType) {
569
+ events = events.filter((e) => e.eventType === filter.eventType);
570
+ }
571
+ if (filter?.sessionId) {
572
+ events = events.filter((e) => e.context?.sessionId === filter.sessionId);
573
+ }
574
+ if (filter?.startTime !== void 0) {
575
+ events = events.filter((e) => e.timestamp >= filter.startTime);
576
+ }
577
+ if (filter?.endTime !== void 0) {
578
+ events = events.filter((e) => e.timestamp <= filter.endTime);
579
+ }
580
+ return events;
581
+ } catch (error) {
582
+ console.error("Failed to retrieve telemetry events", {
583
+ error: error instanceof Error ? error.message : String(error)
584
+ });
585
+ throw error;
586
+ }
587
+ }
588
+ /**
589
+ * Check if a request ID has been processed (idempotency)
590
+ */
591
+ async hasRequestId(requestId) {
592
+ try {
593
+ const result = await this.db.select().from(telemetryIdempotencyKeys).where(eq(telemetryIdempotencyKeys.idempotencyKey, requestId)).limit(1);
594
+ return result.length > 0;
595
+ } catch (error) {
596
+ console.error("Failed to check idempotency key", {
597
+ requestId,
598
+ error: error instanceof Error ? error.message : String(error)
599
+ });
600
+ throw error;
601
+ }
602
+ }
603
+ /**
604
+ * Record a request ID as processed (idempotency)
605
+ * Caches response data for duplicate request handling (2026 best practice)
606
+ */
607
+ async recordRequestId(requestId, responseData = {}) {
608
+ try {
609
+ const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1e3);
610
+ await this.db.insert(telemetryIdempotencyKeys).values({
611
+ idempotencyKey: requestId,
612
+ responseData,
613
+ createdAt: /* @__PURE__ */ new Date(),
614
+ expiresAt
615
+ });
616
+ } catch (error) {
617
+ console.error("Failed to record idempotency key", {
618
+ requestId,
619
+ error: error instanceof Error ? error.message : String(error)
620
+ });
621
+ throw error;
622
+ }
623
+ }
624
+ /**
625
+ * Extract user ID from event context or payload
626
+ */
627
+ extractUserId(event) {
628
+ return event.payload.userId || event.payload.user_id || null;
629
+ }
630
+ /**
631
+ * Extract API key ID from event context or payload
632
+ */
633
+ extractApiKeyId(event) {
634
+ return event.payload.apiKeyId || event.payload.api_key_id || null;
635
+ }
636
+ /**
637
+ * Categorize event based on eventType
638
+ * Maps to eventCategory enum in schema
639
+ */
640
+ categorizeEvent(event) {
641
+ const type = event.eventType.toLowerCase();
642
+ if (type.includes("error")) return "error";
643
+ if (type.includes("feature") || type.includes("usage")) return "feature_usage";
644
+ if (type.includes("lifecycle") || type.includes("session")) return "lifecycle";
645
+ if (type.includes("engagement") || type.includes("interaction")) return "engagement";
646
+ return "system";
647
+ }
648
+ /**
649
+ * Redact sensitive properties before storage (GDPR compliance 2026)
650
+ * Prevents PII leakage through telemetry data
651
+ */
652
+ redactProperties(properties) {
653
+ const sensitiveFields = [
654
+ "email",
655
+ "password",
656
+ "token",
657
+ "apiKey",
658
+ "api_key",
659
+ "secret",
660
+ "accessToken",
661
+ "access_token",
662
+ "refreshToken",
663
+ "refresh_token",
664
+ "creditCard",
665
+ "credit_card",
666
+ "ssn",
667
+ "socialSecurity"
668
+ ];
669
+ const redacted = {
670
+ ...properties
671
+ };
672
+ for (const field of sensitiveFields) {
673
+ if (field in redacted) {
674
+ redacted[field] = "[REDACTED]";
675
+ }
676
+ }
677
+ return redacted;
678
+ }
679
+ /**
680
+ * Transform database row to TelemetryEvent contract type
681
+ */
682
+ toTelemetryEvent(row) {
683
+ return {
684
+ id: row.id,
685
+ eventType: row.eventType,
686
+ payload: row.properties || {},
687
+ timestamp: row.timestamp.getTime(),
688
+ context: {
689
+ sessionId: row.sessionId || void 0,
690
+ requestId: row.requestId || `synthetic-${row.id}`,
691
+ workspaceId: row.workspaceId || void 0,
692
+ client: row.platform || "unknown"
693
+ }
694
+ };
695
+ }
696
+ };
524
697
 
525
698
  // ../../packages/platform/dist/db/database-service.js
526
699
  var databaseService = {
@@ -607,7 +780,7 @@ async function getCapabilities(userId, options = {}) {
607
780
  throw new Error("Database not available");
608
781
  }
609
782
  let capabilities = await db.query.userDetectionCapabilities.findFirst({
610
- where: /* @__PURE__ */ __name((cap, { eq: eq15 }) => eq15(cap.userId, validatedUserId), "where")
783
+ where: /* @__PURE__ */ __name((cap, { eq: eq16 }) => eq16(cap.userId, validatedUserId), "where")
611
784
  });
612
785
  if (!capabilities) {
613
786
  const [created] = await db.insert(userDetectionCapabilities).values({
@@ -830,7 +1003,7 @@ async function getCapabilityAuditHistory(userId, limit = 50) {
830
1003
  return [];
831
1004
  }
832
1005
  return await db.query.capabilityAudit.findMany({
833
- where: /* @__PURE__ */ __name((audit, { eq: eq15 }) => eq15(audit.userId, userId), "where"),
1006
+ where: /* @__PURE__ */ __name((audit, { eq: eq16 }) => eq16(audit.userId, userId), "where"),
834
1007
  orderBy: /* @__PURE__ */ __name((audit, { desc: desc4 }) => desc4(audit.createdAt), "orderBy"),
835
1008
  limit
836
1009
  });
@@ -997,7 +1170,7 @@ async function getOrganizationById(id) {
997
1170
  throw new Error("Database not available");
998
1171
  }
999
1172
  return await db.query.organization.findFirst({
1000
- where: /* @__PURE__ */ __name((org, { eq: eq15 }) => eq15(org.id, id), "where"),
1173
+ where: /* @__PURE__ */ __name((org, { eq: eq16 }) => eq16(org.id, id), "where"),
1001
1174
  with: {
1002
1175
  members: true,
1003
1176
  invitations: true
@@ -1012,7 +1185,7 @@ async function getOrganizationsWithMembers(userId) {
1012
1185
  return await db.query.organization.findMany({
1013
1186
  with: {
1014
1187
  members: {
1015
- where: /* @__PURE__ */ __name((member3, { eq: eq15 }) => eq15(member3.userId, userId), "where"),
1188
+ where: /* @__PURE__ */ __name((member3, { eq: eq16 }) => eq16(member3.userId, userId), "where"),
1016
1189
  with: {
1017
1190
  user: true
1018
1191
  }
@@ -1026,7 +1199,7 @@ async function getInvitationById(id) {
1026
1199
  throw new Error("Database not available");
1027
1200
  }
1028
1201
  return await db.query.invitation.findFirst({
1029
- where: /* @__PURE__ */ __name((invitation2, { eq: eq15 }) => eq15(invitation2.id, id), "where"),
1202
+ where: /* @__PURE__ */ __name((invitation2, { eq: eq16 }) => eq16(invitation2.id, id), "where"),
1030
1203
  with: {
1031
1204
  organization: true
1032
1205
  }
@@ -1038,7 +1211,7 @@ async function getOrganizationBySlug(slug) {
1038
1211
  throw new Error("Database not available");
1039
1212
  }
1040
1213
  return await db.query.organization.findFirst({
1041
- where: /* @__PURE__ */ __name((org, { eq: eq15 }) => eq15(org.slug, slug), "where")
1214
+ where: /* @__PURE__ */ __name((org, { eq: eq16 }) => eq16(org.slug, slug), "where")
1042
1215
  });
1043
1216
  }
1044
1217
  __name(getOrganizationBySlug, "getOrganizationBySlug");
@@ -1047,7 +1220,7 @@ async function getOrganizationMembership(organizationId, userId) {
1047
1220
  throw new Error("Database not available");
1048
1221
  }
1049
1222
  return await db.query.member.findFirst({
1050
- where: /* @__PURE__ */ __name((member3, { and: and6, eq: eq15 }) => and6(eq15(member3.organizationId, organizationId), eq15(member3.userId, userId)), "where"),
1223
+ where: /* @__PURE__ */ __name((member3, { and: and6, eq: eq16 }) => and6(eq16(member3.organizationId, organizationId), eq16(member3.userId, userId)), "where"),
1051
1224
  with: {
1052
1225
  organization: true
1053
1226
  }
@@ -1059,7 +1232,7 @@ async function getOrganizationWithPurchasesAndMembersCount(organizationId) {
1059
1232
  throw new Error("Database not available");
1060
1233
  }
1061
1234
  return await db.query.organization.findFirst({
1062
- where: /* @__PURE__ */ __name((org, { eq: eq15 }) => eq15(org.id, organizationId), "where"),
1235
+ where: /* @__PURE__ */ __name((org, { eq: eq16 }) => eq16(org.id, organizationId), "where"),
1063
1236
  with: {
1064
1237
  purchases: true
1065
1238
  },
@@ -1074,7 +1247,7 @@ async function getPendingInvitationByEmail(email) {
1074
1247
  throw new Error("Database not available");
1075
1248
  }
1076
1249
  return await db.query.invitation.findFirst({
1077
- where: /* @__PURE__ */ __name((invitation2, { and: and6, eq: eq15 }) => and6(eq15(invitation2.email, email), eq15(invitation2.status, "pending")), "where")
1250
+ where: /* @__PURE__ */ __name((invitation2, { and: and6, eq: eq16 }) => and6(eq16(invitation2.email, email), eq16(invitation2.status, "pending")), "where")
1078
1251
  });
1079
1252
  }
1080
1253
  __name(getPendingInvitationByEmail, "getPendingInvitationByEmail");
@@ -1335,7 +1508,7 @@ async function getPurchasesByOrganizationId(organizationId) {
1335
1508
  throw new Error("Database not available");
1336
1509
  }
1337
1510
  return db.query.purchase.findMany({
1338
- where: /* @__PURE__ */ __name((purchase3, { eq: eq15 }) => eq15(purchase3.organizationId, organizationId), "where")
1511
+ where: /* @__PURE__ */ __name((purchase3, { eq: eq16 }) => eq16(purchase3.organizationId, organizationId), "where")
1339
1512
  });
1340
1513
  }
1341
1514
  __name(getPurchasesByOrganizationId, "getPurchasesByOrganizationId");
@@ -1344,7 +1517,7 @@ async function getPurchasesByUserId(userId) {
1344
1517
  throw new Error("Database not available");
1345
1518
  }
1346
1519
  return db.query.purchase.findMany({
1347
- where: /* @__PURE__ */ __name((purchase3, { eq: eq15 }) => eq15(purchase3.userId, userId), "where")
1520
+ where: /* @__PURE__ */ __name((purchase3, { eq: eq16 }) => eq16(purchase3.userId, userId), "where")
1348
1521
  });
1349
1522
  }
1350
1523
  __name(getPurchasesByUserId, "getPurchasesByUserId");
@@ -1353,7 +1526,7 @@ async function getPurchaseById(id) {
1353
1526
  throw new Error("Database not available");
1354
1527
  }
1355
1528
  return db.query.purchase.findFirst({
1356
- where: /* @__PURE__ */ __name((purchase3, { eq: eq15 }) => eq15(purchase3.id, id), "where")
1529
+ where: /* @__PURE__ */ __name((purchase3, { eq: eq16 }) => eq16(purchase3.id, id), "where")
1357
1530
  });
1358
1531
  }
1359
1532
  __name(getPurchaseById, "getPurchaseById");
@@ -1362,7 +1535,7 @@ async function getPurchaseBySubscriptionId(subscriptionId) {
1362
1535
  throw new Error("Database not available");
1363
1536
  }
1364
1537
  return db.query.purchase.findFirst({
1365
- where: /* @__PURE__ */ __name((purchase3, { eq: eq15 }) => eq15(purchase3.subscriptionId, subscriptionId), "where")
1538
+ where: /* @__PURE__ */ __name((purchase3, { eq: eq16 }) => eq16(purchase3.subscriptionId, subscriptionId), "where")
1366
1539
  });
1367
1540
  }
1368
1541
  __name(getPurchaseBySubscriptionId, "getPurchaseBySubscriptionId");
@@ -1438,7 +1611,7 @@ async function getUserById(id) {
1438
1611
  throw new Error("Database not available");
1439
1612
  }
1440
1613
  return await db.query.user.findFirst({
1441
- where: /* @__PURE__ */ __name((user3, { eq: eq15 }) => eq15(user3.id, id), "where")
1614
+ where: /* @__PURE__ */ __name((user3, { eq: eq16 }) => eq16(user3.id, id), "where")
1442
1615
  });
1443
1616
  }
1444
1617
  __name(getUserById, "getUserById");
@@ -1447,7 +1620,7 @@ async function getUserByEmail(email) {
1447
1620
  throw new Error("Database not available");
1448
1621
  }
1449
1622
  return await db.query.user.findFirst({
1450
- where: /* @__PURE__ */ __name((user3, { eq: eq15 }) => eq15(user3.email, email), "where")
1623
+ where: /* @__PURE__ */ __name((user3, { eq: eq16 }) => eq16(user3.email, email), "where")
1451
1624
  });
1452
1625
  }
1453
1626
  __name(getUserByEmail, "getUserByEmail");
@@ -1480,7 +1653,7 @@ async function getAccountById(id) {
1480
1653
  throw new Error("Database not available");
1481
1654
  }
1482
1655
  return await db.query.account.findFirst({
1483
- where: /* @__PURE__ */ __name((account3, { eq: eq15 }) => eq15(account3.id, id), "where")
1656
+ where: /* @__PURE__ */ __name((account3, { eq: eq16 }) => eq16(account3.id, id), "where")
1484
1657
  });
1485
1658
  }
1486
1659
  __name(getAccountById, "getAccountById");
@@ -1541,7 +1714,7 @@ async function getWorkspaceLinkById(workspaceId) {
1541
1714
  }
1542
1715
  const validatedId = workspaceIdSchema.parse(workspaceId);
1543
1716
  return await db.query.workspaceLinks.findFirst({
1544
- where: /* @__PURE__ */ __name((link, { eq: eq15 }) => eq15(link.workspaceId, validatedId), "where")
1717
+ where: /* @__PURE__ */ __name((link, { eq: eq16 }) => eq16(link.workspaceId, validatedId), "where")
1545
1718
  });
1546
1719
  }
1547
1720
  __name(getWorkspaceLinkById, "getWorkspaceLinkById");
@@ -1555,7 +1728,7 @@ async function resolveTierByWorkspaceId(workspaceId) {
1555
1728
  try {
1556
1729
  const validatedId = workspaceIdSchema.parse(workspaceId);
1557
1730
  const link = await db.query.workspaceLinks.findFirst({
1558
- where: /* @__PURE__ */ __name((link2, { eq: eq15 }) => eq15(link2.workspaceId, validatedId), "where")
1731
+ where: /* @__PURE__ */ __name((link2, { eq: eq16 }) => eq16(link2.workspaceId, validatedId), "where")
1559
1732
  });
1560
1733
  if (!link) {
1561
1734
  return {
@@ -1681,7 +1854,7 @@ async function getWorkspaceLinksByUserId(userId) {
1681
1854
  throw new Error("Database not available");
1682
1855
  }
1683
1856
  return await db.query.workspaceLinks.findMany({
1684
- where: /* @__PURE__ */ __name((link, { eq: eq15 }) => eq15(link.userId, userId), "where"),
1857
+ where: /* @__PURE__ */ __name((link, { eq: eq16 }) => eq16(link.userId, userId), "where"),
1685
1858
  orderBy: /* @__PURE__ */ __name((link, { desc: desc4 }) => desc4(link.lastSeenAt), "orderBy")
1686
1859
  });
1687
1860
  }
@@ -1874,7 +2047,7 @@ var extensionSessions = pgTable("extension_sessions", {
1874
2047
  // ../../packages/platform/dist/db/test-utils.js
1875
2048
  var testInTransaction = /* @__PURE__ */ __name((testName, testFn) => {
1876
2049
  return async () => {
1877
- const module = await import('./client-WIO6W447.js');
2050
+ const module = await import('./client-62E3L6DW.js');
1878
2051
  const db2 = module.db;
1879
2052
  try {
1880
2053
  await testFn(db2);
@@ -1898,7 +2071,7 @@ var truncateAllTables = /* @__PURE__ */ __name(async () => {
1898
2071
  console.warn("truncateAllTables is not implemented - tests may have side effects");
1899
2072
  }, "truncateAllTables");
1900
2073
  var getTestDb = /* @__PURE__ */ __name(async () => {
1901
- const module = await import('./client-WIO6W447.js');
2074
+ const module = await import('./client-62E3L6DW.js');
1902
2075
  return module.db;
1903
2076
  }, "getTestDb");
1904
2077
  var closeTestDb = /* @__PURE__ */ __name(async () => {
@@ -4256,4 +4429,4 @@ function createTierUpgradeSagaWithDeps(deps) {
4256
4429
  }
4257
4430
  __name(createTierUpgradeSagaWithDeps, "createTierUpgradeSagaWithDeps");
4258
4431
 
4259
- export { AccountSchema, AiChatSchema, AttributionServiceImpl, ENABLE_CAPTCHA, ENABLE_ENHANCED_2FA, ENABLE_MULTI_SESSION, ENABLE_SSO, EntitlementsServiceImpl, InvitationSchema, MCPService, MemberSchema, OrganizationSchema, OrganizationUpdateSchema, PasskeySchema, PioneerServiceImpl, PurchaseInsertSchema, PurchaseSchema, PurchaseUpdateSchema, SagaOrchestratorImpl, SessionSchema, SnapshotStoreDb, TelemetrySinkDb, UserSchema, UserUpdateSchema, VerificationSchema, anonymizeEmail, anonymizeUserData, anonymizeUserId, appendFalsePositivePatterns, calculateDecayedWeight, cleanupExpiredData, clearCapabilityCache, closeTestDb, config, countAllOrganizations, countAllUsers, createPurchase, createTestUser, createTierUpgradeSagaWithDeps, createUser, createUserAccount, databaseService, deletePurchaseBySubscriptionId, deleteUserApiKeys, deleteUserData, exportUserData, extensionLinkTokens, extensionSessions, findSimilarPatterns, generateOrganizationSlug, getAccountById, getBaseUrl, getCacheMetrics, getCapabilities, getCapabilityAuditHistory, getInvitationById, getMCPService, getOrganizationById, getOrganizationBySlug, getOrganizationMembership, getOrganizationWithPurchasesAndMembersCount, getOrganizations, getOrganizationsWithMembers, getPendingInvitationByEmail, getPurchaseById, getPurchaseBySubscriptionId, getPurchasesByOrganizationId, getPurchasesByUserId, getTestDb, getUserByEmail, getUserById, getUserPrivacyPreferences, getUsers, getVectorStats, getWorkspaceLinkById, getWorkspaceLinksByUserId, handleTierDowngrade, handleTierUpgrade, healthCheck, incrementDetectionsAnalyzed, insertPatternWithEmbedding, invalidateCapabilityCache, isPgvectorEnabled, linkWorkspace, logAnonymizedEvent, logCapabilityAudit, mergeSignalIntoPattern, recordFalsePositiveSignal, resetCacheMetrics, resetCapabilities, resolveTierByWorkspaceId, sagaPersistence, sanitizeForLogging, searchSimilarPatterns, shouldRetainData, signalToPattern, testInTransaction, truncateAllTables, unlinkAllWorkspacesForUser, unlinkWorkspace, updateCapabilities, updateOrganization, updatePatternEmbedding, updatePurchase, updateUser, updateWorkspaceTier };
4432
+ export { AccountSchema, AiChatSchema, AttributionServiceImpl, ENABLE_CAPTCHA, ENABLE_ENHANCED_2FA, ENABLE_MULTI_SESSION, ENABLE_SSO, EntitlementsServiceImpl, InvitationSchema, MCPService, MemberSchema, OrganizationSchema, OrganizationUpdateSchema, PasskeySchema, PioneerServiceImpl, PurchaseInsertSchema, PurchaseSchema, PurchaseUpdateSchema, SagaOrchestratorImpl, SessionSchema, SnapshotStoreDb, TelemetrySinkDb, TelemetrySinkDbAdapter, UserSchema, UserUpdateSchema, VerificationSchema, anonymizeEmail, anonymizeUserData, anonymizeUserId, appendFalsePositivePatterns, calculateDecayedWeight, cleanupExpiredData, clearCapabilityCache, closeTestDb, config, countAllOrganizations, countAllUsers, createPurchase, createTestUser, createTierUpgradeSagaWithDeps, createUser, createUserAccount, databaseService, deletePurchaseBySubscriptionId, deleteUserApiKeys, deleteUserData, exportUserData, extensionLinkTokens, extensionSessions, findSimilarPatterns, generateOrganizationSlug, getAccountById, getBaseUrl, getCacheMetrics, getCapabilities, getCapabilityAuditHistory, getInvitationById, getMCPService, getOrganizationById, getOrganizationBySlug, getOrganizationMembership, getOrganizationWithPurchasesAndMembersCount, getOrganizations, getOrganizationsWithMembers, getPendingInvitationByEmail, getPurchaseById, getPurchaseBySubscriptionId, getPurchasesByOrganizationId, getPurchasesByUserId, getTestDb, getUserByEmail, getUserById, getUserPrivacyPreferences, getUsers, getVectorStats, getWorkspaceLinkById, getWorkspaceLinksByUserId, handleTierDowngrade, handleTierUpgrade, healthCheck, incrementDetectionsAnalyzed, insertPatternWithEmbedding, invalidateCapabilityCache, isPgvectorEnabled, linkWorkspace, logAnonymizedEvent, logCapabilityAudit, mergeSignalIntoPattern, recordFalsePositiveSignal, resetCacheMetrics, resetCapabilities, resolveTierByWorkspaceId, sagaPersistence, sanitizeForLogging, searchSimilarPatterns, shouldRetainData, signalToPattern, testInTransaction, truncateAllTables, unlinkAllWorkspacesForUser, unlinkWorkspace, updateCapabilities, updateOrganization, updatePatternEmbedding, updatePurchase, updateUser, updateWorkspaceTier };
@@ -874,6 +874,32 @@ function resolveNodePath() {
874
874
  }
875
875
  __name(resolveNodePath, "resolveNodePath");
876
876
  __name2(resolveNodePath, "resolveNodePath");
877
+ function resolveSnapbackBinaryPath() {
878
+ try {
879
+ const isWindows = process.platform === "win32";
880
+ const command = isWindows ? "where snapback" : "which snapback";
881
+ const result = execSync(command, {
882
+ encoding: "utf-8",
883
+ timeout: 5e3
884
+ }).trim();
885
+ const binaryPath = result.split(/\r?\n/)[0].trim();
886
+ if (binaryPath && existsSync(binaryPath)) {
887
+ return binaryPath;
888
+ }
889
+ } catch {
890
+ }
891
+ const commonPaths = [
892
+ "/opt/homebrew/bin/snapback",
893
+ "/usr/local/bin/snapback",
894
+ "/usr/bin/snapback"
895
+ ];
896
+ for (const p of commonPaths) {
897
+ if (existsSync(p)) return p;
898
+ }
899
+ return "snapback";
900
+ }
901
+ __name(resolveSnapbackBinaryPath, "resolveSnapbackBinaryPath");
902
+ __name2(resolveSnapbackBinaryPath, "resolveSnapbackBinaryPath");
877
903
  function isCommandExecutable2(command) {
878
904
  if (command.startsWith("/") || command.match(/^[A-Z]:\\/i)) {
879
905
  return existsSync(command);
@@ -1038,7 +1064,7 @@ function getSnapbackMCPConfig(options = {}) {
1038
1064
  args.push("--workspace", workspaceRoot);
1039
1065
  }
1040
1066
  return {
1041
- command: "snapback",
1067
+ command: resolveSnapbackBinaryPath(),
1042
1068
  args,
1043
1069
  ...Object.keys(env).length > 0 && {
1044
1070
  env
@@ -1205,7 +1231,13 @@ function mergeConfig(existing, snapbackConfig, format) {
1205
1231
  }
1206
1232
  };
1207
1233
  case "qoder": {
1208
- const qoderType = snapbackConfig.url ? "sse" : "stdio";
1234
+ let qoderType;
1235
+ if (snapbackConfig.url) {
1236
+ const isLocal = snapbackConfig.url.startsWith("http://localhost") || snapbackConfig.url.startsWith("http://127.0.0.1");
1237
+ qoderType = isLocal ? "sse" : "http";
1238
+ } else {
1239
+ qoderType = "stdio";
1240
+ }
1209
1241
  return {
1210
1242
  ...existing,
1211
1243
  mcpServers: {
@@ -50,7 +50,7 @@ async function emitPostHogEvent(eventType, metadata) {
50
50
  __name(emitPostHogEvent, "emitPostHogEvent");
51
51
  async function writeAuditLog(eventType, metadata) {
52
52
  try {
53
- const { db, snapbackSchema } = await import('./dist-YZBJAYEJ.js');
53
+ const { db, snapbackSchema } = await import('./dist-OO5LJHL6.js');
54
54
  if (!db) {
55
55
  logger.warn("Database not available for audit logging", {
56
56
  eventType
@@ -4202,7 +4202,10 @@ var quarantineEvents = pgTable("quarantine_events", {
4202
4202
  errorReason: text("error_reason").notNull(),
4203
4203
  errorStack: text("error_stack"),
4204
4204
  attemptedAt: timestamp("attempted_at").defaultNow().notNull(),
4205
- createdAt: timestamp("created_at").defaultNow().notNull()
4205
+ createdAt: timestamp("created_at").defaultNow().notNull(),
4206
+ // Retry logic columns (for replayQuarantined implementation)
4207
+ retryCount: integer("retry_count").default(0).notNull(),
4208
+ lastAttemptedAt: timestamp("last_attempted_at")
4206
4209
  }, (table) => {
4207
4210
  return {
4208
4211
  attemptedAtIndex: index("quarantine_events_attempted_at_idx").on(table.attemptedAt),