musememory 3.0.2 → 3.2.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/dist/index.js CHANGED
@@ -4480,7 +4480,7 @@ function searchMemoriesFts(db, query, filters) {
4480
4480
  sql += ` ORDER BY rank ASC`;
4481
4481
  const targetLimit = filters?.limit;
4482
4482
  if (targetLimit) {
4483
- const shouldOverfetch = filters?.overfetch !== false && !filters?.includeSuperseded && !filters?.status;
4483
+ const shouldOverfetch = filters?.overfetch !== false && !filters?.status && (!filters?.includeSuperseded || !filters?.includeConflicted);
4484
4484
  const fetchLimit = shouldOverfetch ? Math.min(500, Math.max(targetLimit * 2, targetLimit + 10)) : targetLimit;
4485
4485
  sql += ` LIMIT ?`;
4486
4486
  params.push(fetchLimit);
@@ -4495,6 +4495,10 @@ function searchMemoriesFts(db, query, filters) {
4495
4495
  hidden_count++;
4496
4496
  continue;
4497
4497
  }
4498
+ if (!filters?.includeConflicted && !filters?.status && entry.status === "conflicted") {
4499
+ hidden_count++;
4500
+ continue;
4501
+ }
4498
4502
  activeHits.push({ entry, rank: r.rank });
4499
4503
  }
4500
4504
  const finalHits = targetLimit ? activeHits.slice(0, targetLimit) : activeHits;
@@ -4601,6 +4605,29 @@ function findConflictBetween(db, memoryA, memoryB) {
4601
4605
  return null;
4602
4606
  return rowToConflictRecord(row);
4603
4607
  }
4608
+ function listConflictsFromDb(db, options) {
4609
+ let sql = `SELECT * FROM conflicts`;
4610
+ const conditions = [];
4611
+ const params = [];
4612
+ if (options?.status) {
4613
+ conditions.push(`status = ?`);
4614
+ params.push(options.status);
4615
+ }
4616
+ if (options?.memoryId) {
4617
+ conditions.push(`(memory_a_id = ? OR memory_b_id = ?)`);
4618
+ params.push(options.memoryId, options.memoryId);
4619
+ }
4620
+ if (conditions.length > 0) {
4621
+ sql += ` WHERE ` + conditions.join(" AND ");
4622
+ }
4623
+ sql += ` ORDER BY detected_at DESC`;
4624
+ if (options?.limit && options.limit > 0) {
4625
+ sql += ` LIMIT ?`;
4626
+ params.push(options.limit);
4627
+ }
4628
+ const rows = db.query(sql).all(params);
4629
+ return rows.map(rowToConflictRecord);
4630
+ }
4604
4631
  function insertFactProvenance(db, record) {
4605
4632
  try {
4606
4633
  const scope = record.scope ?? "global";
@@ -7559,6 +7586,10 @@ function queryContext(store, query = "", options = {}) {
7559
7586
  hidden_count++;
7560
7587
  return false;
7561
7588
  }
7589
+ if (!options.includeConflicted && options.status !== "conflicted" && e.status === "conflicted") {
7590
+ hidden_count++;
7591
+ return false;
7592
+ }
7562
7593
  if (!options.includeArchived && options.status !== "archived" && e.status === "archived") {
7563
7594
  hidden_count++;
7564
7595
  return false;
@@ -7840,7 +7871,7 @@ function searchTemporal(store, query, atTime, options = {}) {
7840
7871
  };
7841
7872
  }
7842
7873
  function formatPromptContext(store, memoryDir, query = "", options = {}) {
7843
- const cacheKey = store.cache ? `ctx:${query}:${options.project || ""}:${options.depth || "L2"}:${options.limit ?? 5}:${options.tokenBudget ?? ""}:${options.type || ""}:${options.status || ""}:${options.verified ? "1" : "0"}:${options.minVeracity ?? ""}:${options.as_of || ""}:${options.includeSuperseded ? "1" : "0"}:${options.includeArchived ? "1" : "0"}:${options.includeUntrusted ? "1" : "0"}:${options.types?.join(",") || ""}:${options.tags?.join(",") || ""}` : undefined;
7874
+ const cacheKey = store.cache ? `ctx:${query}:${options.project || ""}:${options.depth || "L2"}:${options.limit ?? 5}:${options.tokenBudget ?? ""}:${options.type || ""}:${options.status || ""}:${options.verified ? "1" : "0"}:${options.minVeracity ?? ""}:${options.as_of || ""}:${options.includeSuperseded ? "1" : "0"}:${options.includeConflicted ? "1" : "0"}:${options.includeArchived ? "1" : "0"}:${options.includeUntrusted ? "1" : "0"}:${options.types?.join(",") || ""}:${options.tags?.join(",") || ""}` : undefined;
7844
7875
  if (cacheKey && store.cache) {
7845
7876
  const cached = store.cache.getContext(cacheKey);
7846
7877
  if (cached) {
@@ -10305,6 +10336,166 @@ var init_embedders = __esm(() => {
10305
10336
  init_schema();
10306
10337
  });
10307
10338
 
10339
+ // src/quality/scope.ts
10340
+ function canonicalizeScope(scope) {
10341
+ if (!scope)
10342
+ return "";
10343
+ const norm = scope.toLowerCase().trim().replace(/[-_]+/g, " ").replace(/\s+/g, " ");
10344
+ if (SCOPE_ALIASES[norm])
10345
+ return SCOPE_ALIASES[norm];
10346
+ const tokens = norm.split(" ").filter(Boolean);
10347
+ if (tokens.length > 1) {
10348
+ const mapped = tokens.map((token) => SCOPE_ALIASES[token]);
10349
+ if (mapped.every((value) => value && value === mapped[0]))
10350
+ return mapped[0];
10351
+ }
10352
+ return norm;
10353
+ }
10354
+ function extractTrailingScope(text, fromIndex) {
10355
+ const rest = text.slice(fromIndex);
10356
+ const defaultMatch = rest.match(/^[\s,;:()\-]*(?:by\s+default)\b/i);
10357
+ if (defaultMatch)
10358
+ return canonicalizeScope("default");
10359
+ const prepMatch = rest.match(/^[\s,;:()\-]*(?:only\s+)?(instead\s+of|within|throughout|during|while|when|before|after|under|across|between|in|on|at|for|per)\b/i);
10360
+ if (!prepMatch)
10361
+ return;
10362
+ const afterPrep = rest.slice(prepMatch[0].length);
10363
+ const tokens = afterPrep.split(/[^A-Za-z0-9_\-\/]+/).filter(Boolean);
10364
+ if (tokens.length === 0)
10365
+ return;
10366
+ const phrase = [];
10367
+ for (const token of tokens) {
10368
+ if (phrase.length >= SCOPE_MAX_TOKENS)
10369
+ break;
10370
+ const lower = token.toLowerCase();
10371
+ if (phrase.length > 0 && SCOPE_BOUNDARY_TOKENS.has(lower))
10372
+ break;
10373
+ phrase.push(token);
10374
+ }
10375
+ if (phrase.length === 0)
10376
+ return;
10377
+ return canonicalizeScope(phrase.join(" "));
10378
+ }
10379
+ function extractLeadingScope(clause) {
10380
+ const re = /\b(?:only\s+)?(instead\s+of|within|during|while|when|before|after|under|across|between|in|on|at|for|per)\s+([^,;.]{1,40}),/gi;
10381
+ let last;
10382
+ let match;
10383
+ while ((match = re.exec(clause)) !== null) {
10384
+ const tokens = match[2].split(/[^A-Za-z0-9_\-/]+/).filter(Boolean);
10385
+ if (tokens.length === 0)
10386
+ continue;
10387
+ const phrase = [];
10388
+ for (const token of tokens) {
10389
+ if (phrase.length >= SCOPE_MAX_TOKENS)
10390
+ break;
10391
+ if (phrase.length > 0 && SCOPE_BOUNDARY_TOKENS.has(token.toLowerCase()))
10392
+ break;
10393
+ phrase.push(token);
10394
+ }
10395
+ if (phrase.length === 0)
10396
+ continue;
10397
+ last = canonicalizeScope(phrase.join(" "));
10398
+ }
10399
+ return last;
10400
+ }
10401
+ function isStrongScope(scope) {
10402
+ if (!scope)
10403
+ return false;
10404
+ const norm = canonicalizeScope(scope);
10405
+ if (STRONG_SCOPES.has(norm))
10406
+ return true;
10407
+ return /^(tier|level|stage|phase|version|v)\s*\d+$/.test(norm);
10408
+ }
10409
+ function scopesAreCompatible(scopeA, scopeB) {
10410
+ const a = scopeA ? canonicalizeScope(scopeA) : "";
10411
+ const b = scopeB ? canonicalizeScope(scopeB) : "";
10412
+ if (!a && !b)
10413
+ return true;
10414
+ if (!a || !b)
10415
+ return false;
10416
+ return a === b;
10417
+ }
10418
+ var SCOPE_ALIASES, SCOPE_BOUNDARY_TOKENS, SCOPE_MAX_TOKENS = 3, STRONG_SCOPES;
10419
+ var init_scope = __esm(() => {
10420
+ SCOPE_ALIASES = {
10421
+ prod: "production",
10422
+ production: "production",
10423
+ dev: "development",
10424
+ development: "development",
10425
+ test: "testing",
10426
+ testing: "testing",
10427
+ qa: "testing",
10428
+ staging: "staging",
10429
+ stage: "staging",
10430
+ analytics: "analytics",
10431
+ warehouse: "analytics",
10432
+ olap: "analytics",
10433
+ macos: "desktop",
10434
+ desktop: "desktop",
10435
+ mobile: "mobile",
10436
+ ios: "mobile",
10437
+ android: "mobile",
10438
+ frontend: "frontend",
10439
+ backend: "backend",
10440
+ localhost: "local",
10441
+ local: "local",
10442
+ ci: "ci",
10443
+ cd: "ci",
10444
+ container: "container",
10445
+ docker: "container",
10446
+ browser: "browser",
10447
+ server: "server",
10448
+ client: "client"
10449
+ };
10450
+ SCOPE_BOUNDARY_TOKENS = new Set([
10451
+ "and",
10452
+ "but",
10453
+ "or",
10454
+ "so",
10455
+ "because",
10456
+ "since",
10457
+ "unless",
10458
+ "which",
10459
+ "that",
10460
+ "who",
10461
+ "is",
10462
+ "are",
10463
+ "was",
10464
+ "were",
10465
+ "be",
10466
+ "been",
10467
+ "to",
10468
+ "as",
10469
+ "if",
10470
+ "then",
10471
+ "otherwise",
10472
+ "however",
10473
+ "meanwhile",
10474
+ "also",
10475
+ "both",
10476
+ "either",
10477
+ "neither"
10478
+ ]);
10479
+ STRONG_SCOPES = new Set([
10480
+ "production",
10481
+ "development",
10482
+ "testing",
10483
+ "staging",
10484
+ "analytics",
10485
+ "desktop",
10486
+ "mobile",
10487
+ "frontend",
10488
+ "backend",
10489
+ "local",
10490
+ "ci",
10491
+ "container",
10492
+ "browser",
10493
+ "server",
10494
+ "client",
10495
+ "default"
10496
+ ]);
10497
+ });
10498
+
10308
10499
  // src/quality/invalidation.ts
10309
10500
  function normalizeTerm(term) {
10310
10501
  if (!term)
@@ -10315,10 +10506,6 @@ function canonicalizeRelation(rel) {
10315
10506
  const norm = normalizeTerm(rel);
10316
10507
  return RELATION_ALIASES[norm] || norm;
10317
10508
  }
10318
- function canonicalizeScope(scope) {
10319
- const norm = normalizeTerm(scope);
10320
- return SCOPE_ALIASES[norm] || norm;
10321
- }
10322
10509
  function extractFactTriples(entry) {
10323
10510
  if (entry.triples && Array.isArray(entry.triples) && entry.triples.length > 0) {
10324
10511
  return entry.triples.map((t) => ({
@@ -10331,32 +10518,32 @@ function extractFactTriples(entry) {
10331
10518
  const triples = [];
10332
10519
  const text = `${entry.title} ${entry.content}`;
10333
10520
  const lowerText = text.toLowerCase();
10334
- let detectedScope;
10521
+ let tagScope;
10335
10522
  if (entry.tags) {
10336
10523
  for (const tag of entry.tags) {
10337
10524
  if (tag.startsWith("scope:")) {
10338
- detectedScope = canonicalizeScope(tag.replace("scope:", ""));
10525
+ tagScope = canonicalizeScope(tag.replace("scope:", ""));
10339
10526
  break;
10340
10527
  }
10341
10528
  }
10342
10529
  }
10343
- if (!detectedScope) {
10344
- if (/\b(in\s+production|for\s+production|prod\s+server|production\s+database|production\s+environment)\b/i.test(text)) {
10345
- detectedScope = "production";
10346
- } else if (/\b(in\s+development|for\s+development|dev\s+server|development\s+environment|local\s+dev)\b/i.test(text)) {
10347
- detectedScope = "development";
10348
- } else if (/\b(for\s+analytics|analytics\s+warehouse|analytics\s+database|in\s+olap)\b/i.test(text)) {
10349
- detectedScope = "analytics";
10350
- } else if (/\b(on\s+macos|macos\s+desktop|for\s+desktop)\b/i.test(text)) {
10351
- detectedScope = "desktop";
10352
- } else if (/\b(on\s+mobile|mobile\s+ios|on\s+ios|on\s+android|mobile\s+app)\b/i.test(text)) {
10353
- detectedScope = "mobile";
10354
- } else if (/\b(in\s+staging|staging\s+environment)\b/i.test(text)) {
10355
- detectedScope = "staging";
10356
- } else if (/\b(in\s+testing|for\s+test|test\s+environment)\b/i.test(text)) {
10357
- detectedScope = "testing";
10358
- }
10359
- }
10530
+ let fixedScope;
10531
+ if (/\b(in\s+production|for\s+production|prod\s+server|production\s+database|production\s+environment)\b/i.test(text)) {
10532
+ fixedScope = "production";
10533
+ } else if (/\b(in\s+development|for\s+development|dev\s+server|development\s+environment|local\s+dev)\b/i.test(text)) {
10534
+ fixedScope = "development";
10535
+ } else if (/\b(for\s+analytics|analytics\s+warehouse|analytics\s+database|in\s+olap)\b/i.test(text)) {
10536
+ fixedScope = "analytics";
10537
+ } else if (/\b(on\s+macos|macos\s+desktop|for\s+desktop)\b/i.test(text)) {
10538
+ fixedScope = "desktop";
10539
+ } else if (/\b(on\s+mobile|mobile\s+ios|on\s+ios|on\s+android|mobile\s+app)\b/i.test(text)) {
10540
+ fixedScope = "mobile";
10541
+ } else if (/\b(in\s+staging|staging\s+environment)\b/i.test(text)) {
10542
+ fixedScope = "staging";
10543
+ } else if (/\b(in\s+testing|for\s+test|test\s+environment)\b/i.test(text)) {
10544
+ fixedScope = "testing";
10545
+ }
10546
+ const scopeFor = (claimEndIndex) => tagScope ?? fixedScope ?? extractTrailingScope(text, claimEndIndex);
10360
10547
  const dbMatch = text.match(/\b(primary\s+database|database|warehouse|persistence|db)\b.*?uses?\s+([A-Za-z0-9_]+)/i) || text.match(/\buses?\s+(postgresql|postgres|mysql|sqlite|mongodb)\b/i);
10361
10548
  if (dbMatch) {
10362
10549
  const target = dbMatch[2] ? dbMatch[2].toLowerCase() : dbMatch[1].toLowerCase();
@@ -10364,7 +10551,7 @@ function extractFactTriples(entry) {
10364
10551
  entity: "database",
10365
10552
  relation: "database",
10366
10553
  target: target.startsWith("postgres") ? "postgresql" : target,
10367
- scope: detectedScope
10554
+ scope: scopeFor((dbMatch.index ?? 0) + dbMatch[0].length)
10368
10555
  });
10369
10556
  }
10370
10557
  const portMatch = text.match(/\b(binds?\s+to\s+port|runs?\s+on\s+port|port\s+binding|port)\s+([0-9]{2,5})\b/i);
@@ -10373,7 +10560,7 @@ function extractFactTriples(entry) {
10373
10560
  entity: "gateway",
10374
10561
  relation: "port",
10375
10562
  target: portMatch[2],
10376
- scope: detectedScope
10563
+ scope: scopeFor((portMatch.index ?? 0) + portMatch[0].length)
10377
10564
  });
10378
10565
  }
10379
10566
  const themeMatch = text.match(/\b(theme|mode)\b.*?\b(dark|light)\b/i) || text.match(/\bprefers?\s+(dark|light)\s+(mode|theme)?\b/i);
@@ -10383,7 +10570,7 @@ function extractFactTriples(entry) {
10383
10570
  entity: "user_theme",
10384
10571
  relation: "theme",
10385
10572
  target,
10386
- scope: detectedScope
10573
+ scope: scopeFor((themeMatch.index ?? 0) + themeMatch[0].length)
10387
10574
  });
10388
10575
  }
10389
10576
  const runtimeMatch = text.match(/\b(runtime|engine)\b.*?\b(bun|node|deno)\b/i) || text.match(/\b(bun|node|deno)\s+runtime\b/i);
@@ -10393,7 +10580,7 @@ function extractFactTriples(entry) {
10393
10580
  entity: "runtime",
10394
10581
  relation: "runtime",
10395
10582
  target,
10396
- scope: detectedScope
10583
+ scope: scopeFor((runtimeMatch.index ?? 0) + runtimeMatch[0].length)
10397
10584
  });
10398
10585
  }
10399
10586
  const statusMatch = text.match(/\b([A-Za-z0-9_-]+)\s+(service|feature|caching|compression|plugin)\s*(?:is\s+)?(enabled|disabled|allowed|denied|forbidden|active|inactive)\b/i);
@@ -10402,7 +10589,7 @@ function extractFactTriples(entry) {
10402
10589
  entity: normalizeTerm(statusMatch[1]),
10403
10590
  relation: "status",
10404
10591
  target: normalizeTerm(statusMatch[3]),
10405
- scope: detectedScope
10592
+ scope: scopeFor((statusMatch.index ?? 0) + statusMatch[0].length)
10406
10593
  });
10407
10594
  }
10408
10595
  return triples;
@@ -10463,52 +10650,10 @@ function evaluateScopedAlignment(tripleA, tripleB) {
10463
10650
  reason: `Target values '${tripleA.target}' and '${tripleB.target}' are consistent`
10464
10651
  };
10465
10652
  }
10466
- function evaluateScopedInvalidation(existing, candidate) {
10467
- const existingTriples = extractFactTriples(existing);
10468
- const candidateTriples = extractFactTriples(candidate);
10469
- if (existingTriples.length > 0 && candidateTriples.length > 0) {
10470
- let hadScopeOrEntityMismatch = false;
10471
- let mismatchReason = "";
10472
- for (const eTriple of existingTriples) {
10473
- for (const cTriple of candidateTriples) {
10474
- const alignment = evaluateScopedAlignment(eTriple, cTriple);
10475
- if (alignment.aligned && alignment.conflicting) {
10476
- return {
10477
- shouldInvalidate: true,
10478
- aligned: true,
10479
- conflicting: true,
10480
- confidence: 0.95,
10481
- reason: alignment.reason,
10482
- matchingTriples: {
10483
- existingTriple: eTriple,
10484
- candidateTriple: cTriple
10485
- }
10486
- };
10487
- } else if (!alignment.aligned) {
10488
- hadScopeOrEntityMismatch = true;
10489
- mismatchReason = alignment.reason || "Entity or scope mismatch";
10490
- }
10491
- }
10492
- }
10493
- if (hadScopeOrEntityMismatch) {
10494
- return {
10495
- shouldInvalidate: false,
10496
- aligned: false,
10497
- conflicting: false,
10498
- confidence: 0,
10499
- reason: mismatchReason
10500
- };
10501
- }
10502
- }
10503
- return {
10504
- shouldInvalidate: false,
10505
- aligned: false,
10506
- conflicting: false,
10507
- confidence: 0
10508
- };
10509
- }
10510
- var RELATION_ALIASES, SCOPE_ALIASES, CONFLICTING_TARGET_PAIRS;
10653
+ var RELATION_ALIASES, CONFLICTING_TARGET_PAIRS;
10511
10654
  var init_invalidation = __esm(() => {
10655
+ init_scope();
10656
+ init_scope();
10512
10657
  RELATION_ALIASES = {
10513
10658
  db: "database",
10514
10659
  database: "database",
@@ -10531,25 +10676,6 @@ var init_invalidation = __esm(() => {
10531
10676
  runtime: "runtime",
10532
10677
  runtime_engine: "runtime"
10533
10678
  };
10534
- SCOPE_ALIASES = {
10535
- prod: "production",
10536
- production: "production",
10537
- dev: "development",
10538
- development: "development",
10539
- test: "testing",
10540
- testing: "testing",
10541
- staging: "staging",
10542
- analytics: "analytics",
10543
- warehouse: "analytics",
10544
- olap: "analytics",
10545
- macos: "desktop",
10546
- desktop: "desktop",
10547
- mobile: "mobile",
10548
- ios: "mobile",
10549
- android: "mobile",
10550
- frontend: "frontend",
10551
- backend: "backend"
10552
- };
10553
10679
  CONFLICTING_TARGET_PAIRS = [
10554
10680
  [/\b(enable|enabled|true|allow|allowed|active)\b/i, /\b(disable|disabled|false|deny|denied|inactive|forbidden)\b/i],
10555
10681
  [/\b(postgres|postgresql)\b/i, /\b(mysql|sqlite|mongodb)\b/i],
@@ -10561,10 +10687,570 @@ var init_invalidation = __esm(() => {
10561
10687
  ];
10562
10688
  });
10563
10689
 
10690
+ // src/quality/polarity.ts
10691
+ function tokenize2(text) {
10692
+ return text.split(/[^A-Za-z0-9_\-/]+/).filter(Boolean);
10693
+ }
10694
+ function lastClauseBoundary(text) {
10695
+ let index = -1;
10696
+ for (const boundary of CLAUSE_BOUNDARIES) {
10697
+ const found = text.lastIndexOf(boundary);
10698
+ if (found > index)
10699
+ index = found;
10700
+ }
10701
+ return index;
10702
+ }
10703
+ function trimTrailing(tokens, vocab) {
10704
+ const out = [...tokens];
10705
+ while (out.length && vocab.has(out[out.length - 1].toLowerCase()))
10706
+ out.pop();
10707
+ return out;
10708
+ }
10709
+ function subjectBefore(text, claimIndex, maxTokens = 4) {
10710
+ const before = text.slice(0, claimIndex);
10711
+ const clause = before.slice(lastClauseBoundary(before) + 1);
10712
+ let tokens = tokenize2(clause);
10713
+ tokens = trimTrailing(tokens, AUXILIARY_TOKENS);
10714
+ tokens = trimTrailing(tokens, TRAILING_VERB_TOKENS);
10715
+ tokens = trimTrailing(tokens, TRAILING_PREPOSITION_TOKENS);
10716
+ tokens = tokens.slice(-maxTokens);
10717
+ tokens = tokens.filter((token) => !TECHNOLOGY_TOKENS.has(token.toLowerCase()));
10718
+ while (tokens.length && LEADING_TRIM_TOKENS.has(tokens[0].toLowerCase()))
10719
+ tokens.shift();
10720
+ tokens = trimTrailing(tokens, TRAILING_PREPOSITION_TOKENS);
10721
+ return tokens.join(" ").toLowerCase();
10722
+ }
10723
+ function objectAfter(text, fromIndex) {
10724
+ const rest = text.slice(fromIndex);
10725
+ const tokens = [];
10726
+ let consumed = fromIndex;
10727
+ const re = /[\s:,\-]*([A-Za-z0-9_\-/]+)/g;
10728
+ let match;
10729
+ while ((match = re.exec(rest)) !== null) {
10730
+ const word = match[1];
10731
+ const lower = word.toLowerCase();
10732
+ if (tokens.length >= 4)
10733
+ break;
10734
+ if (tokens.length > 0 && (TRAILING_PREPOSITION_TOKENS.has(lower) || AUXILIARY_TOKENS.has(lower)))
10735
+ break;
10736
+ if (tokens.length === 0 && DETERMINER_TOKENS.has(lower)) {
10737
+ consumed = fromIndex + match.index + match[0].length;
10738
+ continue;
10739
+ }
10740
+ tokens.push(word);
10741
+ consumed = fromIndex + match.index + match[0].length;
10742
+ }
10743
+ if (tokens.length === 0)
10744
+ return null;
10745
+ return { phrase: tokens.join(" ").toLowerCase(), endIndex: consumed };
10746
+ }
10747
+ function isMigrationContext(before) {
10748
+ const tail = before.slice(-48).toLowerCase();
10749
+ return /\b(from|migrat\w*|switch\w*|move[ds]?|chang\w*|replac\w*|instead\s+of|convert\w*|port\w*)\b[^.]{0,30}$/.test(tail);
10750
+ }
10751
+ function resolveScope(text, claimEnd, claimStart, options) {
10752
+ const strongOnly = options?.strongOnly === true;
10753
+ const trailing = extractTrailingScope(text, claimEnd);
10754
+ if (trailing && (!strongOnly || isStrongScope(trailing)))
10755
+ return trailing;
10756
+ const before = text.slice(0, claimStart);
10757
+ const clause = before.slice(lastClauseBoundary(before) + 1);
10758
+ const leading = extractLeadingScope(clause);
10759
+ if (leading && (!strongOnly || isStrongScope(leading)))
10760
+ return leading;
10761
+ return;
10762
+ }
10763
+ function statusClaims(text) {
10764
+ const claims = [];
10765
+ const adjectiveRe = new RegExp(`\\b(${STATUS_ADJECTIVES.join("|")})\\b`, "gi");
10766
+ let match;
10767
+ while ((match = adjectiveRe.exec(text)) !== null) {
10768
+ const term = match[1].toLowerCase();
10769
+ const entity = subjectBefore(text, match.index);
10770
+ if (!entity)
10771
+ continue;
10772
+ const scope = resolveScope(text, match.index + match[0].length, match.index);
10773
+ claims.push({
10774
+ entity,
10775
+ relation: "status",
10776
+ target: term,
10777
+ scope,
10778
+ evidence: match[0]
10779
+ });
10780
+ }
10781
+ const booleanRe = /\b(is|are|was|were|=|:)\s+(true|false)\b/gi;
10782
+ while ((match = booleanRe.exec(text)) !== null) {
10783
+ const term = match[2].toLowerCase();
10784
+ const entity = subjectBefore(text, match.index);
10785
+ if (!entity)
10786
+ continue;
10787
+ const scope = resolveScope(text, match.index + match[0].length, match.index);
10788
+ claims.push({
10789
+ entity,
10790
+ relation: "status",
10791
+ target: term,
10792
+ scope,
10793
+ evidence: match[0]
10794
+ });
10795
+ }
10796
+ const verbRe = /\b(enables?|disables?|allows?|denies?)\b/gi;
10797
+ while ((match = verbRe.exec(text)) !== null) {
10798
+ const verb = match[1].toLowerCase();
10799
+ const target = VERB_STATUS[verb];
10800
+ if (!target)
10801
+ continue;
10802
+ const object = objectAfter(text, match.index + match[0].length);
10803
+ if (!object)
10804
+ continue;
10805
+ const scope = resolveScope(text, object.endIndex, match.index);
10806
+ claims.push({
10807
+ entity: object.phrase,
10808
+ relation: "status",
10809
+ target,
10810
+ scope,
10811
+ evidence: `${match[0]} ${object.phrase}`
10812
+ });
10813
+ }
10814
+ return claims;
10815
+ }
10816
+ function databaseClaims(text) {
10817
+ const claims = [];
10818
+ const re = /\b(postgres(?:ql)?|mysql|sqlite|mongodb)\b/gi;
10819
+ let match;
10820
+ while ((match = re.exec(text)) !== null) {
10821
+ const token = match[1].toLowerCase();
10822
+ const before = text.slice(0, match.index);
10823
+ const after = text.slice(match.index + match[0].length);
10824
+ if (isMigrationContext(before))
10825
+ continue;
10826
+ const anchored = DB_ANCHOR_BEFORE.test(before) || DB_ANCHOR_AFTER.test(after);
10827
+ if (!anchored)
10828
+ continue;
10829
+ const scope = resolveScope(text, match.index + match[0].length, match.index, { strongOnly: true });
10830
+ claims.push({
10831
+ entity: subjectBefore(text, match.index, 3) || "database",
10832
+ relation: "database",
10833
+ target: DB_TARGETS[token] ?? token,
10834
+ scope,
10835
+ evidence: match[0]
10836
+ });
10837
+ }
10838
+ return claims;
10839
+ }
10840
+ function portClaims(text) {
10841
+ const claims = [];
10842
+ const re = /\bport\s+(\d{2,5})\b/gi;
10843
+ let match;
10844
+ while ((match = re.exec(text)) !== null) {
10845
+ const entity = subjectBefore(text, match.index, 3);
10846
+ if (!entity)
10847
+ continue;
10848
+ const scope = resolveScope(text, match.index + match[0].length, match.index, { strongOnly: true });
10849
+ claims.push({
10850
+ entity,
10851
+ relation: "port",
10852
+ target: match[1],
10853
+ scope,
10854
+ evidence: match[0]
10855
+ });
10856
+ }
10857
+ return claims;
10858
+ }
10859
+ function protocolClaims(text) {
10860
+ const claims = [];
10861
+ const re = /\b(rest\s+api|graphql|grpc|rest)\b/gi;
10862
+ let match;
10863
+ while ((match = re.exec(text)) !== null) {
10864
+ const token = match[1].toLowerCase();
10865
+ if (isMigrationContext(text.slice(0, match.index)))
10866
+ continue;
10867
+ const before = text.slice(0, match.index);
10868
+ const clause = text.slice(lastClauseBoundary(before) + 1);
10869
+ const clauseWithoutMatch = `${clause}${text.slice(match.index + match[0].length).split(/[.;\n]/)[0]}`;
10870
+ if (!PROTOCOL_ANCHOR.test(clauseWithoutMatch))
10871
+ continue;
10872
+ const entity = subjectBefore(text, match.index, 3);
10873
+ if (!entity)
10874
+ continue;
10875
+ const target = token.startsWith("rest") ? "rest" : token;
10876
+ const scope = resolveScope(text, match.index + match[0].length, match.index, { strongOnly: true });
10877
+ claims.push({
10878
+ entity,
10879
+ relation: "protocol",
10880
+ target,
10881
+ scope,
10882
+ evidence: match[0]
10883
+ });
10884
+ }
10885
+ return claims;
10886
+ }
10887
+ function runtimeClaims(text) {
10888
+ const claims = [];
10889
+ const re = /\b(bun|node|deno)\s+runtime\b/gi;
10890
+ let match;
10891
+ while ((match = re.exec(text)) !== null) {
10892
+ if (isMigrationContext(text.slice(0, match.index)))
10893
+ continue;
10894
+ const entity = subjectBefore(text, match.index, 3);
10895
+ if (!entity)
10896
+ continue;
10897
+ const scope = resolveScope(text, match.index + match[0].length, match.index, { strongOnly: true });
10898
+ claims.push({
10899
+ entity,
10900
+ relation: "runtime",
10901
+ target: match[1].toLowerCase(),
10902
+ scope,
10903
+ evidence: match[0]
10904
+ });
10905
+ }
10906
+ return claims;
10907
+ }
10908
+ function themeClaims(text) {
10909
+ const claims = [];
10910
+ const re = /\b(dark|light)\s+(?:mode|theme)\b/gi;
10911
+ let match;
10912
+ while ((match = re.exec(text)) !== null) {
10913
+ const entity = subjectBefore(text, match.index, 3);
10914
+ if (!entity)
10915
+ continue;
10916
+ const scope = resolveScope(text, match.index + match[0].length, match.index, { strongOnly: true });
10917
+ claims.push({
10918
+ entity,
10919
+ relation: "theme",
10920
+ target: match[1].toLowerCase(),
10921
+ scope,
10922
+ evidence: match[0]
10923
+ });
10924
+ }
10925
+ return claims;
10926
+ }
10927
+ function policyClaims(text) {
10928
+ const claims = [];
10929
+ const re = /\b(always|never)\b/gi;
10930
+ let match;
10931
+ while ((match = re.exec(text)) !== null) {
10932
+ const object = objectAfter(text, match.index + match[0].length);
10933
+ if (!object)
10934
+ continue;
10935
+ claims.push({
10936
+ entity: object.phrase,
10937
+ relation: "policy",
10938
+ target: match[1].toLowerCase(),
10939
+ scope: undefined,
10940
+ evidence: `${match[0]} ${object.phrase}`
10941
+ });
10942
+ }
10943
+ return claims;
10944
+ }
10945
+ function dedupeClaims(claims) {
10946
+ const scopedKeys = new Set;
10947
+ for (const claim of claims) {
10948
+ if (claim.scope)
10949
+ scopedKeys.add(`${claim.entity}|${claim.relation}|${claim.target}`);
10950
+ }
10951
+ const seen = new Set;
10952
+ const unique = [];
10953
+ for (const claim of claims) {
10954
+ const key = `${claim.entity}|${claim.relation}|${claim.target}|${claim.scope ?? ""}`;
10955
+ if (seen.has(key))
10956
+ continue;
10957
+ if (!claim.scope && scopedKeys.has(`${claim.entity}|${claim.relation}|${claim.target}`))
10958
+ continue;
10959
+ seen.add(key);
10960
+ unique.push(claim);
10961
+ }
10962
+ return unique;
10963
+ }
10964
+ function extractPolarityClaims(text) {
10965
+ if (!text)
10966
+ return [];
10967
+ const claims = [
10968
+ ...statusClaims(text),
10969
+ ...databaseClaims(text),
10970
+ ...portClaims(text),
10971
+ ...protocolClaims(text),
10972
+ ...runtimeClaims(text),
10973
+ ...themeClaims(text),
10974
+ ...policyClaims(text)
10975
+ ];
10976
+ return dedupeClaims(claims);
10977
+ }
10978
+ function comparePolarityClaims(a, b) {
10979
+ if (a.relation !== b.relation)
10980
+ return { conflicting: false };
10981
+ if (!a.entity || !b.entity)
10982
+ return { conflicting: false };
10983
+ const entityMatches = a.entity === b.entity || a.entity.includes(b.entity) || b.entity.includes(a.entity);
10984
+ if (!entityMatches) {
10985
+ return {
10986
+ conflicting: false,
10987
+ reason: `Subject mismatch: '${a.entity}' vs '${b.entity}' are different subjects`
10988
+ };
10989
+ }
10990
+ if (!scopesAreCompatible(a.scope, b.scope)) {
10991
+ return {
10992
+ conflicting: false,
10993
+ reason: `Distinct scopes: '${a.scope ?? "unscoped"}' vs '${b.scope ?? "unscoped"}' represent distinct contextual truths, not a contradiction`
10994
+ };
10995
+ }
10996
+ if (areTargetsContradictory(a.target, b.target)) {
10997
+ const label = RELATION_LABELS[a.relation] ?? "Contradictory claims";
10998
+ return {
10999
+ conflicting: true,
11000
+ reason: `${label}: contradictory ${a.relation} values '${a.target}' vs '${b.target}' for subject '${a.entity}'${a.scope ? ` in scope '${a.scope}'` : ""}`
11001
+ };
11002
+ }
11003
+ return { conflicting: false };
11004
+ }
11005
+ var DETERMINER_TOKENS, AUXILIARY_TOKENS, TRAILING_PREPOSITION_TOKENS, LEADING_TRIM_TOKENS, TRAILING_VERB_TOKENS, CLAUSE_BOUNDARIES, TECHNOLOGY_TOKENS, STATUS_ADJECTIVES, VERB_STATUS, DB_TARGETS, DB_ANCHOR_BEFORE, DB_ANCHOR_AFTER, PROTOCOL_ANCHOR, RELATION_LABELS;
11006
+ var init_polarity = __esm(() => {
11007
+ init_invalidation();
11008
+ init_scope();
11009
+ DETERMINER_TOKENS = new Set([
11010
+ "the",
11011
+ "a",
11012
+ "an",
11013
+ "this",
11014
+ "that",
11015
+ "these",
11016
+ "those",
11017
+ "our",
11018
+ "its",
11019
+ "their",
11020
+ "my",
11021
+ "your",
11022
+ "some",
11023
+ "any",
11024
+ "each",
11025
+ "every",
11026
+ "all",
11027
+ "both",
11028
+ "one",
11029
+ "other",
11030
+ "no",
11031
+ "most",
11032
+ "such"
11033
+ ]);
11034
+ AUXILIARY_TOKENS = new Set([
11035
+ "is",
11036
+ "are",
11037
+ "was",
11038
+ "were",
11039
+ "be",
11040
+ "been",
11041
+ "being",
11042
+ "am",
11043
+ "remains",
11044
+ "remain",
11045
+ "remained",
11046
+ "stays",
11047
+ "stay",
11048
+ "stayed",
11049
+ "get",
11050
+ "gets",
11051
+ "got",
11052
+ "getting",
11053
+ "should",
11054
+ "must",
11055
+ "needs",
11056
+ "need",
11057
+ "to",
11058
+ "has",
11059
+ "have",
11060
+ "had",
11061
+ "will",
11062
+ "would",
11063
+ "can",
11064
+ "could",
11065
+ "may",
11066
+ "might",
11067
+ "do",
11068
+ "does",
11069
+ "did",
11070
+ "not",
11071
+ "also",
11072
+ "then",
11073
+ "than",
11074
+ "so",
11075
+ "if",
11076
+ "when",
11077
+ "while",
11078
+ "because",
11079
+ "since",
11080
+ "unless",
11081
+ "which",
11082
+ "that",
11083
+ "where",
11084
+ "how",
11085
+ "what",
11086
+ "why",
11087
+ "and",
11088
+ "or",
11089
+ "but",
11090
+ "there",
11091
+ "it",
11092
+ "its",
11093
+ "we",
11094
+ "you",
11095
+ "they"
11096
+ ]);
11097
+ TRAILING_PREPOSITION_TOKENS = new Set([
11098
+ "for",
11099
+ "in",
11100
+ "on",
11101
+ "at",
11102
+ "of",
11103
+ "with",
11104
+ "to",
11105
+ "from",
11106
+ "by",
11107
+ "during",
11108
+ "when",
11109
+ "while",
11110
+ "under",
11111
+ "within",
11112
+ "across",
11113
+ "per",
11114
+ "about",
11115
+ "into",
11116
+ "onto",
11117
+ "via",
11118
+ "using",
11119
+ "after",
11120
+ "before",
11121
+ "since",
11122
+ "until",
11123
+ "against",
11124
+ "between",
11125
+ "among",
11126
+ "like",
11127
+ "as",
11128
+ "than",
11129
+ "and",
11130
+ "or",
11131
+ "but",
11132
+ "so",
11133
+ "because",
11134
+ "over",
11135
+ "under"
11136
+ ]);
11137
+ LEADING_TRIM_TOKENS = new Set([
11138
+ ...DETERMINER_TOKENS,
11139
+ "for",
11140
+ "about",
11141
+ "regarding",
11142
+ "concerning",
11143
+ "with",
11144
+ "using",
11145
+ "via",
11146
+ "when",
11147
+ "while",
11148
+ "because",
11149
+ "since",
11150
+ "if"
11151
+ ]);
11152
+ TRAILING_VERB_TOKENS = new Set([
11153
+ "bind",
11154
+ "binds",
11155
+ "binding",
11156
+ "run",
11157
+ "runs",
11158
+ "running",
11159
+ "listen",
11160
+ "listens",
11161
+ "listening",
11162
+ "serve",
11163
+ "serves",
11164
+ "serving",
11165
+ "use",
11166
+ "uses",
11167
+ "using",
11168
+ "connect",
11169
+ "connects",
11170
+ "connecting",
11171
+ "expose",
11172
+ "exposes",
11173
+ "exposing",
11174
+ "prefers",
11175
+ "prefer",
11176
+ "prefers",
11177
+ "target",
11178
+ "targets",
11179
+ "targeting",
11180
+ "serve",
11181
+ "serves",
11182
+ "provide",
11183
+ "provides",
11184
+ "providing",
11185
+ "keep",
11186
+ "keeps",
11187
+ "keeping",
11188
+ "always",
11189
+ "never"
11190
+ ]);
11191
+ CLAUSE_BOUNDARIES = [".", ";", `
11192
+ `, "!", "?", "|"];
11193
+ TECHNOLOGY_TOKENS = new Set([
11194
+ "postgres",
11195
+ "postgresql",
11196
+ "mysql",
11197
+ "sqlite",
11198
+ "mongodb",
11199
+ "bun",
11200
+ "node",
11201
+ "deno",
11202
+ "rest",
11203
+ "graphql",
11204
+ "grpc",
11205
+ "dark",
11206
+ "light"
11207
+ ]);
11208
+ STATUS_ADJECTIVES = [
11209
+ "enabled",
11210
+ "disabled",
11211
+ "active",
11212
+ "inactive",
11213
+ "allowed",
11214
+ "denied",
11215
+ "forbidden"
11216
+ ];
11217
+ VERB_STATUS = {
11218
+ enable: "enabled",
11219
+ enables: "enabled",
11220
+ disable: "disabled",
11221
+ disables: "disabled",
11222
+ allow: "allowed",
11223
+ allows: "allowed",
11224
+ deny: "denied",
11225
+ denies: "denied"
11226
+ };
11227
+ DB_TARGETS = {
11228
+ postgres: "postgresql",
11229
+ postgresql: "postgresql",
11230
+ mysql: "mysql",
11231
+ sqlite: "sqlite",
11232
+ mongodb: "mongodb"
11233
+ };
11234
+ DB_ANCHOR_BEFORE = /\b(?:uses?|using|use\s+of|primary\s+database|database|db|engine|store|persistence|persisted\s+(?:in|to)|backed\s+by|runs?\s+on)\b[^A-Za-z0-9]{0,30}$/i;
11235
+ DB_ANCHOR_AFTER = /^\s*(?:is|as)\s+(?:the\s+)?(?:primary\s+)?(?:database|store|engine|db)\b/i;
11236
+ PROTOCOL_ANCHOR = /\b(api|protocol|service|endpoint|interface|route|client|server)\b/i;
11237
+ RELATION_LABELS = {
11238
+ status: "Opposing configuration polarity",
11239
+ database: "Conflicting primary database choice",
11240
+ port: "Conflicting port binding",
11241
+ protocol: "Conflicting communication protocol",
11242
+ runtime: "Conflicting runtime engine",
11243
+ theme: "Conflicting theme selection",
11244
+ policy: "Contradictory universal policy"
11245
+ };
11246
+ });
11247
+
10564
11248
  // src/quality/contradiction.ts
10565
11249
  import { randomUUID } from "node:crypto";
10566
- function detectConflict(store, candidate) {
10567
- const candidateText = `${candidate.title} ${candidate.content}`.toLowerCase();
11250
+ function detectConflict(store, candidate, options) {
11251
+ const dryRun = options?.dryRun === true;
11252
+ const candidateText = `${candidate.title || ""} ${candidate.content || ""}`;
11253
+ const candidateClaims = extractPolarityClaims(candidateText);
10568
11254
  const entries = list(store, candidate.project ? { project: candidate.project } : undefined);
10569
11255
  for (const entry of entries) {
10570
11256
  if (candidate.id && entry.id === candidate.id)
@@ -10572,55 +11258,93 @@ function detectConflict(store, candidate) {
10572
11258
  if (entry.status !== "active" && entry.status !== "confirmed" && entry.status !== "conflicted") {
10573
11259
  continue;
10574
11260
  }
10575
- const scopedCheck = evaluateScopedInvalidation(entry, candidate);
10576
- if (scopedCheck.shouldInvalidate) {
10577
- const entryText = `${entry.title} ${entry.content}`.toLowerCase();
10578
- let pairReason = "";
10579
- for (const [patternA, patternB, reason] of CONTRADICTORY_PAIRS) {
10580
- if (patternA.test(candidateText) && patternB.test(entryText) || patternB.test(candidateText) && patternA.test(entryText)) {
10581
- pairReason = `${reason} between candidate and "${entry.title}" (${entry.id})`;
10582
- break;
11261
+ const existingTriples = entry.triples && entry.triples.length > 0 ? extractFactTriples(entry) : [];
11262
+ const candidateTriples = candidate.triples && candidate.triples.length > 0 ? extractFactTriples(candidate) : [];
11263
+ if (existingTriples.length > 0 && candidateTriples.length > 0) {
11264
+ for (const existingTriple of existingTriples) {
11265
+ for (const candidateTriple of candidateTriples) {
11266
+ const alignment = evaluateScopedAlignment(existingTriple, candidateTriple);
11267
+ if (alignment.aligned && alignment.conflicting) {
11268
+ return {
11269
+ conflicted: true,
11270
+ conflictingEntry: entry,
11271
+ confidence: 0.95,
11272
+ reason: alignment.reason,
11273
+ dryRun
11274
+ };
11275
+ }
10583
11276
  }
10584
11277
  }
10585
- const combinedReason = pairReason ? `${pairReason}: ${scopedCheck.reason}` : scopedCheck.reason || `Scoped contradiction detected with "${entry.title}" (${entry.id})`;
10586
- return {
10587
- conflicted: true,
10588
- conflictingEntry: entry,
10589
- confidence: scopedCheck.confidence,
10590
- reason: combinedReason
10591
- };
10592
- }
10593
- if (scopedCheck.reason && !scopedCheck.aligned && !scopedCheck.shouldInvalidate) {
10594
- continue;
10595
11278
  }
10596
- const entryText = `${entry.title} ${entry.content}`.toLowerCase();
10597
- const topicSimilarity = computeJaccardSimilarity(candidate.title, entry.title);
10598
- if (topicSimilarity >= 0.4 || entry.type && candidate.type && entry.type === candidate.type && topicSimilarity >= 0.3) {
10599
- for (const [patternA, patternB, reason] of CONTRADICTORY_PAIRS) {
10600
- const aInCand = patternA.test(candidateText);
10601
- const bInCand = patternB.test(candidateText);
10602
- const aInEntry = patternA.test(entryText);
10603
- const bInEntry = patternB.test(entryText);
10604
- if (aInCand && bInEntry || bInCand && aInEntry) {
11279
+ const entryText = `${entry.title || ""} ${entry.content || ""}`;
11280
+ const entryClaims = extractPolarityClaims(entryText);
11281
+ for (const candidateClaim of candidateClaims) {
11282
+ for (const entryClaim of entryClaims) {
11283
+ const comparison = comparePolarityClaims(candidateClaim, entryClaim);
11284
+ if (comparison.conflicting) {
10605
11285
  return {
10606
11286
  conflicted: true,
10607
11287
  conflictingEntry: entry,
10608
11288
  confidence: 0.85,
10609
- reason: `${reason} between candidate and "${entry.title}" (${entry.id})`
11289
+ reason: comparison.reason,
11290
+ dryRun
10610
11291
  };
10611
11292
  }
10612
11293
  }
10613
- if (candidateText.includes("do not") && !entryText.includes("do not") && topicSimilarity > 0.6 || !candidateText.includes("do not") && entryText.includes("do not") && topicSimilarity > 0.6) {
10614
- return {
10615
- conflicted: true,
10616
- conflictingEntry: entry,
10617
- confidence: 0.8,
10618
- reason: `Direct negation detected on topic "${entry.title}" (${entry.id})`
10619
- };
11294
+ }
11295
+ const candidateLower = candidateText.toLowerCase();
11296
+ const entryLower = entryText.toLowerCase();
11297
+ const topicSimilarity = computeJaccardSimilarity(candidate.title || "", entry.title || "");
11298
+ if (topicSimilarity > 0.6 && (candidateLower.includes("do not") && !entryLower.includes("do not") || !candidateLower.includes("do not") && entryLower.includes("do not"))) {
11299
+ return {
11300
+ conflicted: true,
11301
+ conflictingEntry: entry,
11302
+ confidence: 0.8,
11303
+ reason: `Direct negation detected on topic "${entry.title}" (${entry.id})`,
11304
+ dryRun
11305
+ };
11306
+ }
11307
+ }
11308
+ return { conflicted: false, confidence: 0, dryRun };
11309
+ }
11310
+ function scanPotentialConflicts(store, options) {
11311
+ const entries = list(store, options?.project ? { project: options.project } : undefined).filter((entry) => entry.status === "active" || entry.status === "confirmed");
11312
+ const claimsById = new Map;
11313
+ for (const entry of entries) {
11314
+ claimsById.set(entry.id, extractPolarityClaims(`${entry.title || ""} ${entry.content || ""}`));
11315
+ }
11316
+ const found = [];
11317
+ for (let i = 0;i < entries.length; i++) {
11318
+ for (let j = i + 1;j < entries.length; j++) {
11319
+ const a = entries[i];
11320
+ const b = entries[j];
11321
+ const aClaims = claimsById.get(a.id) ?? [];
11322
+ const bClaims = claimsById.get(b.id) ?? [];
11323
+ let conflict = null;
11324
+ for (const aClaim of aClaims) {
11325
+ for (const bClaim of bClaims) {
11326
+ const comparison = comparePolarityClaims(aClaim, bClaim);
11327
+ if (comparison.conflicting) {
11328
+ conflict = comparison;
11329
+ break;
11330
+ }
11331
+ }
11332
+ if (conflict)
11333
+ break;
11334
+ }
11335
+ if (conflict) {
11336
+ found.push({
11337
+ entry_a_id: a.id,
11338
+ entry_a_title: a.title,
11339
+ entry_b_id: b.id,
11340
+ entry_b_title: b.title,
11341
+ confidence: 0.85,
11342
+ reason: conflict.reason
11343
+ });
10620
11344
  }
10621
11345
  }
10622
11346
  }
10623
- return { conflicted: false, confidence: 0 };
11347
+ return options?.limit !== undefined ? found.slice(0, options.limit) : found;
10624
11348
  }
10625
11349
  function flagConflict(store, existingId, newId, reason, options) {
10626
11350
  const existing = get(store, existingId);
@@ -10780,22 +11504,18 @@ function resolveConflict(store, options) {
10780
11504
  message
10781
11505
  };
10782
11506
  }
10783
- var CONTRADICTORY_PAIRS;
11507
+ function listConflicts(store, options) {
11508
+ if (!store.db)
11509
+ return [];
11510
+ return listConflictsFromDb(store.db, options);
11511
+ }
10784
11512
  var init_contradiction = __esm(() => {
10785
11513
  init_store();
10786
11514
  init_audit();
10787
11515
  init_dedup();
10788
11516
  init_sqlite();
10789
11517
  init_invalidation();
10790
- CONTRADICTORY_PAIRS = [
10791
- [/\b(enable|enabled|true)\b/i, /\b(disable|disabled|false)\b/i, "Opposing configuration polarity"],
10792
- [/\b(allow|allowed)\b/i, /\b(deny|denied|disallow|disallowed|forbidden)\b/i, "Opposing permission rule"],
10793
- [/\b(uses?\s+postgresql|postgres)\b/i, /\b(uses?\s+mysql|uses?\s+sqlite|uses?\s+mongodb)\b/i, "Conflicting primary database choice"],
10794
- [/\b(port\s+3000)\b/i, /\b(port\s+8080|port\s+5000|port\s+4000)\b/i, "Conflicting port binding"],
10795
- [/\b(rest\s+api)\b/i, /\b(graphql\s+only|grpc\s+only)\b/i, "Conflicting communication protocol"],
10796
- [/\b(bun\s+runtime)\b/i, /\b(node\s+runtime\s+only|deno\s+only)\b/i, "Conflicting runtime engine"],
10797
- [/\b(always)\b/i, /\b(never)\b/i, "Contradictory universal policy"]
10798
- ];
11518
+ init_polarity();
10799
11519
  });
10800
11520
 
10801
11521
  // src/quality/retention.ts
@@ -13142,6 +13862,9 @@ async function rankAndRetrieveMemories(store, query, options = {}) {
13142
13862
  if (!options.includeSuperseded) {
13143
13863
  entries = entries.filter((e) => e.status !== "superseded" && e.status !== "rejected");
13144
13864
  }
13865
+ if (!options.includeConflicted) {
13866
+ entries = entries.filter((e) => e.status !== "conflicted");
13867
+ }
13145
13868
  if (options.sessionId && options.suppressReinjection !== false) {
13146
13869
  const filtered = defaultReinjectionTracker.filterSuppressed(options.sessionId, entries, {
13147
13870
  turn: options.sessionTurn,
@@ -13441,6 +14164,8 @@ function passesRowFilter(entry, o, now) {
13441
14164
  return false;
13442
14165
  if (!o.includeSuperseded && (entry.status === "superseded" || entry.status === "rejected"))
13443
14166
  return false;
14167
+ if (!o.includeConflicted && entry.status === "conflicted")
14168
+ return false;
13444
14169
  if (!o.includeArchived && entry.status === "archived")
13445
14170
  return false;
13446
14171
  if (!o.includeExpired && isExpired(entry, now))
@@ -13926,6 +14651,10 @@ function list(store, filters) {
13926
14651
  }
13927
14652
  return entries;
13928
14653
  }
14654
+ function listForProject(store, project) {
14655
+ const trimmed = project?.trim();
14656
+ return trimmed ? list(store, { project: trimmed }) : list(store);
14657
+ }
13929
14658
  function countMemories(store) {
13930
14659
  if (store.circuit?.isDegraded() && store.cache) {
13931
14660
  return store.cache.size();
@@ -14318,14 +15047,30 @@ function propose(store, opts) {
14318
15047
  }
14319
15048
  }
14320
15049
  if (opts.checkConflict) {
15050
+ const conflictDryRun = opts.conflictDryRun === true;
14321
15051
  const conflict = detectConflict(store, {
14322
15052
  title: entry.title,
14323
15053
  content: entry.content,
14324
15054
  project: entry.project,
14325
15055
  type: entry.type,
14326
15056
  id: entry.id
14327
- });
15057
+ }, { dryRun: conflictDryRun });
14328
15058
  if (conflict.conflicted && conflict.conflictingEntry) {
15059
+ if (conflictDryRun) {
15060
+ if (store.memoryDir) {
15061
+ recordAuditEvent(store.memoryDir, {
15062
+ operation: "conflict_detected_dry_run",
15063
+ entry_id: entry.id,
15064
+ project: entry.project,
15065
+ details: {
15066
+ would_conflict_with: conflict.conflictingEntry.id,
15067
+ reason: conflict.reason || "Contradiction detected",
15068
+ confidence: conflict.confidence
15069
+ }
15070
+ });
15071
+ }
15072
+ return entry;
15073
+ }
14329
15074
  const flagged = flagConflict(store, conflict.conflictingEntry.id, entry.id, conflict.reason || "Contradiction detected");
14330
15075
  return flagged.incoming;
14331
15076
  }
@@ -18686,7 +19431,7 @@ async function getLatestNpmVersion(packageName = "musememory") {
18686
19431
  try {
18687
19432
  const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
18688
19433
  headers: { Accept: "application/json" },
18689
- signal: AbortSignal.timeout(3000)
19434
+ signal: AbortSignal.timeout(2000)
18690
19435
  });
18691
19436
  if (res.ok) {
18692
19437
  const data = await res.json();
@@ -18698,6 +19443,7 @@ async function getLatestNpmVersion(packageName = "musememory") {
18698
19443
  try {
18699
19444
  const out = execFileSync("npm", ["show", packageName, "version"], {
18700
19445
  encoding: "utf8",
19446
+ timeout: 1500,
18701
19447
  stdio: ["ignore", "pipe", "ignore"]
18702
19448
  }).trim();
18703
19449
  if (out && !out.includes("error"))
@@ -26181,7 +26927,6 @@ function getStandardAgentTranscriptDirs(home = homedir5()) {
26181
26927
  join36(home, ".local", "share", "opencode", "transcripts"),
26182
26928
  join36(home, ".claude", "transcripts"),
26183
26929
  join36(home, ".claude", "projects"),
26184
- join36(home, ".config", "LIFEOS", "runtime", "transcripts"),
26185
26930
  join36(home, ".hermes", "sessions"),
26186
26931
  join36(home, ".hermes", "transcripts"),
26187
26932
  join36(home, ".config", "goose", "sessions"),
@@ -27312,7 +28057,7 @@ var init_pack = __esm(() => {
27312
28057
  // src/retrieval/tree-index.ts
27313
28058
  import { existsSync as existsSync43, readFileSync as readFileSync37, writeFileSync as writeFileSync26, mkdirSync as mkdirSync25, rmSync } from "node:fs";
27314
28059
  import { join as join40 } from "node:path";
27315
- function tokenize2(text) {
28060
+ function tokenize3(text) {
27316
28061
  if (!text || typeof text !== "string")
27317
28062
  return [];
27318
28063
  return text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
@@ -27546,10 +28291,10 @@ function selectPartitions(index, options) {
27546
28291
  return result;
27547
28292
  }
27548
28293
  function scoreNode(node, query) {
27549
- const queryTokens = new Set(tokenize2(query));
28294
+ const queryTokens = new Set(tokenize3(query));
27550
28295
  if (queryTokens.size === 0)
27551
28296
  return 0;
27552
- const haystack = tokenize2(`${node.title} ${node.summary} ${(node.metadata?.tags ?? []).join(" ")}`);
28297
+ const haystack = tokenize3(`${node.title} ${node.summary} ${(node.metadata?.tags ?? []).join(" ")}`);
27553
28298
  const hay = new Set(haystack);
27554
28299
  let overlap = 0;
27555
28300
  for (const t of queryTokens)
@@ -28019,6 +28764,7 @@ class RetrievalEngine {
28019
28764
  limit: options.limit !== undefined ? clampLimit(options.limit, 10, 200, 1) : 10,
28020
28765
  project: options.project,
28021
28766
  includeSuperseded: options.includeSuperseded,
28767
+ includeConflicted: options.includeConflicted,
28022
28768
  includeArchived: options.includeArchived,
28023
28769
  includeExpired: options.includeExpired,
28024
28770
  now: options.now
@@ -29032,8 +29778,8 @@ function attachAnchorToMemory(store, memoryId, anchor, actor = "agent", options
29032
29778
  }
29033
29779
  return entry;
29034
29780
  }
29035
- function auditMemoryAnchors(store, workspaceRoot) {
29036
- const entries = list(store);
29781
+ function auditMemoryAnchors(store, workspaceRoot, options) {
29782
+ const entries = listForProject(store, options?.project);
29037
29783
  const details = [];
29038
29784
  let validCount = 0;
29039
29785
  let driftedCount = 0;
@@ -29181,8 +29927,8 @@ function extractExportedSymbols(filePath) {
29181
29927
  }
29182
29928
  return symbols;
29183
29929
  }
29184
- function detectDocumentationCodeDrift(store, workspaceRoot) {
29185
- const allEntries = list(store);
29930
+ function detectDocumentationCodeDrift(store, workspaceRoot, options) {
29931
+ const allEntries = listForProject(store, options?.project);
29186
29932
  const items = [];
29187
29933
  let documentedCount = 0;
29188
29934
  let implementedCount = 0;
@@ -29314,7 +30060,7 @@ function scanFiles(dir, maxFiles = 50) {
29314
30060
  walk(dir);
29315
30061
  return files;
29316
30062
  }
29317
- function analyzeTechnicalDebt(store, workspaceRoot) {
30063
+ function analyzeTechnicalDebt(store, workspaceRoot, options) {
29318
30064
  const items = [];
29319
30065
  const hotspotMap = new Map;
29320
30066
  const sourceFiles = scanFiles(workspaceRoot, 40);
@@ -29358,7 +30104,7 @@ function analyzeTechnicalDebt(store, workspaceRoot) {
29358
30104
  }
29359
30105
  }
29360
30106
  }
29361
- const allEntries = list(store);
30107
+ const allEntries = listForProject(store, options?.project);
29362
30108
  for (const entry of allEntries) {
29363
30109
  if (entry.status === "archived" || entry.status === "superseded")
29364
30110
  continue;
@@ -29426,8 +30172,8 @@ function classifyBug(entry) {
29426
30172
  }
29427
30173
  return "general_bug";
29428
30174
  }
29429
- function clusterRecurringBugsAndFriction(store) {
29430
- const entries = list(store);
30175
+ function clusterRecurringBugsAndFriction(store, options) {
30176
+ const entries = listForProject(store, options?.project);
29431
30177
  const bugEntries = entries.filter((e) => e.status !== "archived" && (e.type === "fix" || e.type === "negative" || e.negative != null || e.tags?.includes("bug") || e.tags?.includes("fix") || e.tags?.includes("failure") || e.tags?.includes("error")));
29432
30178
  const clusterMap = new Map;
29433
30179
  for (const entry of bugEntries) {
@@ -29533,9 +30279,10 @@ function scoreToStatus(score) {
29533
30279
  return "WARN";
29534
30280
  return "FAIL";
29535
30281
  }
29536
- function evaluateProjectHealth(store, workspaceRoot) {
30282
+ function evaluateProjectHealth(store, workspaceRoot, options) {
29537
30283
  const now = new Date().toISOString();
29538
- const entries = list(store);
30284
+ const projectFilter = options?.project?.trim() || undefined;
30285
+ const entries = listForProject(store, projectFilter);
29539
30286
  const checklist = [];
29540
30287
  let activeCount = 0;
29541
30288
  let confirmedCount = 0;
@@ -29578,7 +30325,7 @@ function evaluateProjectHealth(store, workspaceRoot) {
29578
30325
  },
29579
30326
  remediation_items: integrityRemediations
29580
30327
  };
29581
- const anchorAudit = auditMemoryAnchors(store, workspaceRoot);
30328
+ const anchorAudit = auditMemoryAnchors(store, workspaceRoot, { project: projectFilter });
29582
30329
  const anchorScore = Math.round(anchorAudit.integrity_score * 100);
29583
30330
  const anchorRemediations = [];
29584
30331
  if (anchorAudit.drifted_anchors > 0) {
@@ -29601,7 +30348,7 @@ function evaluateProjectHealth(store, workspaceRoot) {
29601
30348
  },
29602
30349
  remediation_items: anchorRemediations
29603
30350
  };
29604
- const driftAudit = detectDocumentationCodeDrift(store, workspaceRoot);
30351
+ const driftAudit = detectDocumentationCodeDrift(store, workspaceRoot, { project: projectFilter });
29605
30352
  const driftScore = Math.round(driftAudit.alignment_score * 100);
29606
30353
  const driftRemediations = [];
29607
30354
  if (driftAudit.missing_count > 0) {
@@ -29655,8 +30402,8 @@ function evaluateProjectHealth(store, workspaceRoot) {
29655
30402
  },
29656
30403
  remediation_items: negativeRemediations
29657
30404
  };
29658
- const debtReport = analyzeTechnicalDebt(store, workspaceRoot);
29659
- const bugClusters = clusterRecurringBugsAndFriction(store);
30405
+ const debtReport = analyzeTechnicalDebt(store, workspaceRoot, { project: projectFilter });
30406
+ const bugClusters = clusterRecurringBugsAndFriction(store, { project: projectFilter });
29660
30407
  const topFragility = bugClusters.length > 0 ? bugClusters[0].fragility_score : 0;
29661
30408
  const debtPenalty = debtReport.debt_score + Math.round(topFragility * 20);
29662
30409
  const debtScore = Math.max(0, Math.min(100, 100 - debtPenalty));
@@ -29682,7 +30429,16 @@ function evaluateProjectHealth(store, workspaceRoot) {
29682
30429
  checklist.push(...docCodeAlignmentPillar.remediation_items);
29683
30430
  checklist.push(...negativeAntiPatternsPillar.remediation_items);
29684
30431
  checklist.push(...techDebtPillar.remediation_items);
29685
- const projectName = entries[0]?.project || "default";
30432
+ const projectsCovered = [
30433
+ ...new Set(entries.map((e) => e.project).filter((p) => Boolean(p)))
30434
+ ].sort();
30435
+ const projectName = projectFilter ?? (projectsCovered.length === 1 ? projectsCovered[0] : "all");
30436
+ const scope = {
30437
+ filter: projectFilter ?? null,
30438
+ projects: projectsCovered,
30439
+ entry_count: entries.length,
30440
+ code_scope: "workspace"
30441
+ };
29686
30442
  let summary = `Project health evaluated with overall Grade ${overallGrade} (${overallScore}/100) — Status: ${gateStatus}.`;
29687
30443
  if (gateStatus === "PASS") {
29688
30444
  summary += " All 5 pillars meet production hardening criteria.";
@@ -29691,8 +30447,10 @@ function evaluateProjectHealth(store, workspaceRoot) {
29691
30447
  } else {
29692
30448
  summary += " Critical architectural violations or unverified contradictions must be resolved before sign-off.";
29693
30449
  }
30450
+ summary += ` Scope: ${projectFilter ? `project "${projectFilter}"` : "entire store"} (${entries.length} memories across ${projectsCovered.length} project${projectsCovered.length === 1 ? "" : "s"}); code-side metrics cover the workspace.`;
29694
30451
  return {
29695
30452
  project: projectName,
30453
+ scope,
29696
30454
  timestamp: now,
29697
30455
  overall_score: overallScore,
29698
30456
  overall_grade: overallGrade,
@@ -29919,10 +30677,35 @@ var init_reconcile = __esm(() => {
29919
30677
  init_resolver();
29920
30678
  });
29921
30679
 
30680
+ // src/health/resolve.ts
30681
+ function listProjectNames(store) {
30682
+ return [
30683
+ ...new Set(list(store).map((e) => e.project).filter((p) => Boolean(p)))
30684
+ ].sort();
30685
+ }
30686
+ function resolveProjectName(store, requested) {
30687
+ const name = requested?.trim();
30688
+ if (!name)
30689
+ return;
30690
+ const projects = listProjectNames(store);
30691
+ if (projects.includes(name))
30692
+ return name;
30693
+ const matches = projects.filter((p) => p.toLowerCase() === name.toLowerCase());
30694
+ return matches.length === 1 ? matches[0] : undefined;
30695
+ }
30696
+ function formatAvailable(store) {
30697
+ const available = listProjectNames(store);
30698
+ return available.length > 0 ? available.join(", ") : "none";
30699
+ }
30700
+ var init_resolve = __esm(() => {
30701
+ init_store();
30702
+ });
30703
+
29922
30704
  // src/health/index.ts
29923
30705
  var init_health = __esm(() => {
29924
30706
  init_gate();
29925
30707
  init_reconcile();
30708
+ init_resolve();
29926
30709
  });
29927
30710
  // src/adrs/engine.ts
29928
30711
  import { randomUUID as randomUUID3 } from "node:crypto";
@@ -31290,7 +32073,18 @@ function startUiServer(opts) {
31290
32073
  const workspaceRoot = opts.store.layout?.root || getStorageLayout(opts.memoryDir).root;
31291
32074
  if (pathname === "/api/health" && req.method === "GET") {
31292
32075
  try {
31293
- const report = evaluateProjectHealth(opts.store, workspaceRoot);
32076
+ const requestedProject = url.searchParams.get("project")?.trim() || undefined;
32077
+ const canonicalProject = resolveProjectName(opts.store, requestedProject);
32078
+ if (requestedProject && canonicalProject === undefined) {
32079
+ res.writeHead(404, { "Content-Type": "application/json" });
32080
+ res.end(JSON.stringify({
32081
+ error: `no memories for project "${requestedProject}" — available: ${formatAvailable(opts.store)}`
32082
+ }));
32083
+ return;
32084
+ }
32085
+ const report = evaluateProjectHealth(opts.store, workspaceRoot, {
32086
+ project: canonicalProject
32087
+ });
31294
32088
  res.writeHead(200, { "Content-Type": "application/json" });
31295
32089
  res.end(JSON.stringify(report));
31296
32090
  } catch (err) {
@@ -31312,7 +32106,18 @@ function startUiServer(opts) {
31312
32106
  }
31313
32107
  if (pathname === "/api/drift" && req.method === "GET") {
31314
32108
  try {
31315
- const drift = detectDocumentationCodeDrift(opts.store, workspaceRoot);
32109
+ const requestedProject = url.searchParams.get("project")?.trim() || undefined;
32110
+ const canonicalProject = resolveProjectName(opts.store, requestedProject);
32111
+ if (requestedProject && canonicalProject === undefined) {
32112
+ res.writeHead(404, { "Content-Type": "application/json" });
32113
+ res.end(JSON.stringify({
32114
+ error: `no memories for project "${requestedProject}" — available: ${formatAvailable(opts.store)}`
32115
+ }));
32116
+ return;
32117
+ }
32118
+ const drift = detectDocumentationCodeDrift(opts.store, workspaceRoot, {
32119
+ project: canonicalProject
32120
+ });
31316
32121
  res.writeHead(200, { "Content-Type": "application/json" });
31317
32122
  res.end(JSON.stringify(drift));
31318
32123
  } catch (err) {
@@ -31340,7 +32145,18 @@ function startUiServer(opts) {
31340
32145
  }
31341
32146
  if (pathname === "/api/cognition/clusters" && req.method === "GET") {
31342
32147
  try {
31343
- const clusters = clusterRecurringBugsAndFriction(opts.store);
32148
+ const requestedProject = url.searchParams.get("project")?.trim() || undefined;
32149
+ const canonicalProject = resolveProjectName(opts.store, requestedProject);
32150
+ if (requestedProject && canonicalProject === undefined) {
32151
+ res.writeHead(404, { "Content-Type": "application/json" });
32152
+ res.end(JSON.stringify({
32153
+ error: `no memories for project "${requestedProject}" — available: ${formatAvailable(opts.store)}`
32154
+ }));
32155
+ return;
32156
+ }
32157
+ const clusters = clusterRecurringBugsAndFriction(opts.store, {
32158
+ project: canonicalProject
32159
+ });
31344
32160
  res.writeHead(200, { "Content-Type": "application/json" });
31345
32161
  res.end(JSON.stringify({ total_clusters: clusters.length, clusters }));
31346
32162
  } catch (err) {
@@ -31351,7 +32167,18 @@ function startUiServer(opts) {
31351
32167
  }
31352
32168
  if (pathname === "/api/cognition/debt" && req.method === "GET") {
31353
32169
  try {
31354
- const debt = analyzeTechnicalDebt(opts.store, workspaceRoot);
32170
+ const requestedProject = url.searchParams.get("project")?.trim() || undefined;
32171
+ const canonicalProject = resolveProjectName(opts.store, requestedProject);
32172
+ if (requestedProject && canonicalProject === undefined) {
32173
+ res.writeHead(404, { "Content-Type": "application/json" });
32174
+ res.end(JSON.stringify({
32175
+ error: `no memories for project "${requestedProject}" — available: ${formatAvailable(opts.store)}`
32176
+ }));
32177
+ return;
32178
+ }
32179
+ const debt = analyzeTechnicalDebt(opts.store, workspaceRoot, {
32180
+ project: canonicalProject
32181
+ });
31355
32182
  res.writeHead(200, { "Content-Type": "application/json" });
31356
32183
  res.end(JSON.stringify(debt));
31357
32184
  } catch (err) {
@@ -31589,6 +32416,10 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
31589
32416
 
31590
32417
  /* Stats & Quick Actions */
31591
32418
  .header-actions { display: flex; align-items: center; gap: 12px; }
32419
+ .scope-filter { display: flex; align-items: center; gap: 8px; }
32420
+ .scope-filter-label { font-size: 10px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap; }
32421
+ .scope-filter-input { width: 140px; background: #090d14; border: 1px solid var(--panel-border); border-radius: var(--radius-md); padding: 6px 10px; color: #fff; font-size: 12px; outline: none; transition: var(--transition); }
32422
+ .scope-filter-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
31592
32423
  .status-badge { font-size: 12px; font-weight: 600; background: rgba(16,185,129,0.12); color: var(--green); padding: 5px 12px; border-radius: var(--radius-full); border: 1px solid rgba(16,185,129,0.25); display: flex; align-items: center; gap: 6px; }
31593
32424
  .status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); box-shadow: 0 0 8px var(--green); }
31594
32425
  .btn-icon { background: rgba(255,255,255,0.04); border: 1px solid var(--panel-border); color: var(--text-muted); padding: 6px 12px; border-radius: var(--radius-md); font-size: 12px; font-weight: 600; cursor: pointer; transition: var(--transition); display: inline-flex; align-items: center; gap: 6px; }
@@ -31795,6 +32626,10 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
31795
32626
  </div>
31796
32627
 
31797
32628
  <div class="header-actions">
32629
+ <div class="scope-filter">
32630
+ <label class="scope-filter-label" for="projectFilter">Project Scope</label>
32631
+ <input type="text" id="projectFilter" class="scope-filter-input" placeholder="all projects" autocomplete="off" spellcheck="false" title="Leave empty to audit the whole memory store, or enter a project name to scope the health gate, drift, bug cluster and technical debt audits." />
32632
+ </div>
31798
32633
  <button class="btn-icon btn-refresh" id="refreshBtn" onclick="refreshMemories()" title="Refresh and sync active constraints, handoff state, and prior memories">
31799
32634
  <span class="refresh-icon" id="refreshIcon">\uD83D\uDD04</span>
31800
32635
  <span id="refreshLabel">Refresh</span>
@@ -31977,6 +32812,10 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
31977
32812
  <span class="gate-badge gate-pass" id="healthGate">[PASS]</span>
31978
32813
  </div>
31979
32814
  <div style="font-size:13px; color:var(--text-muted); margin-top:3px;" id="healthSummary">Comprehensive project cognitive health evaluation.</div>
32815
+ <div style="font-size:11px; color:var(--text-dim); margin-top:6px; display:flex; gap:14px; flex-wrap:wrap;">
32816
+ <span id="healthScopeProject">Project: --</span>
32817
+ <span id="healthScope">Scope: --</span>
32818
+ </div>
31980
32819
  </div>
31981
32820
  </div>
31982
32821
  <div style="text-align:right;">
@@ -32213,6 +33052,36 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
32213
33052
  let searchQuery = '';
32214
33053
  let currentTab = 'graph';
32215
33054
 
33055
+ // T-321: optional project scope for the studio's audit surfaces.
33056
+ // An empty value means the whole store; a value scopes the health gate,
33057
+ // drift, bug cluster and technical debt audits to that memory project.
33058
+ // The initial value comes from the URL query string so a scoped link
33059
+ // stays shareable and reloadable.
33060
+ let projectScope = (new URLSearchParams(window.location.search).get('project') || '').trim();
33061
+
33062
+ function projectScopeQuery() {
33063
+ return projectScope ? '?project=' + encodeURIComponent(projectScope) : '';
33064
+ }
33065
+
33066
+ function syncProjectScopeUrl() {
33067
+ try {
33068
+ const url = new URL(window.location.href);
33069
+ if (projectScope) url.searchParams.set('project', projectScope);
33070
+ else url.searchParams.delete('project');
33071
+ window.history.replaceState({}, '', url.toString());
33072
+ } catch (err) {
33073
+ console.error('Failed to sync project scope in URL:', err);
33074
+ }
33075
+ }
33076
+
33077
+ function applyProjectScope(value) {
33078
+ projectScope = (value || '').trim();
33079
+ syncProjectScopeUrl();
33080
+ fetchHealth();
33081
+ if (currentTab === 'adrs') fetchDrift();
33082
+ if (currentTab === 'cognition') fetchCognition();
33083
+ }
33084
+
32216
33085
  // ---- Graph state (3D force-directed) ----
32217
33086
  const TYPE_COLORS = {
32218
33087
  fix: '#10b981',
@@ -32879,7 +33748,7 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
32879
33748
  // R15 Studio Loaders
32880
33749
  async function fetchHealth() {
32881
33750
  try {
32882
- const res = await fetch('/api/health');
33751
+ const res = await fetch('/api/health' + projectScopeQuery());
32883
33752
  const data = await res.json();
32884
33753
  renderHealth(data);
32885
33754
  } catch (err) {
@@ -32887,12 +33756,25 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
32887
33756
  }
32888
33757
  }
32889
33758
 
33759
+ // T-321: mirrors the CLI scope wording — "N memories across M project(s)".
33760
+ function formatHealthScope(scope) {
33761
+ if (!scope) return 'Scope: --';
33762
+ const count = scope.entry_count || 0;
33763
+ const projectCount = (scope.projects || []).length;
33764
+ const memories = count === 1 ? 'memory' : 'memories';
33765
+ const projects = projectCount === 1 ? 'project' : 'projects';
33766
+ const label = scope.filter ? 'project "' + scope.filter + '"' : 'entire store';
33767
+ return 'Scope: ' + label + ' (' + count + ' ' + memories + ' across ' + projectCount + ' ' + projects + '); code-side metrics cover the ' + (scope.code_scope || 'workspace') + '.';
33768
+ }
33769
+
32890
33770
  function renderHealth(data) {
32891
33771
  if (!data) return;
32892
33772
  const gradeEl = document.getElementById('healthGrade');
32893
33773
  const scoreEl = document.getElementById('healthScore');
32894
33774
  const gateEl = document.getElementById('healthGate');
32895
33775
  const summaryEl = document.getElementById('healthSummary');
33776
+ const scopeEl = document.getElementById('healthScope');
33777
+ const scopeProjectEl = document.getElementById('healthScopeProject');
32896
33778
  const grade = data.overall_grade || data.grade || '--';
32897
33779
  const score = data.overall_score !== undefined ? data.overall_score : (data.score || 0);
32898
33780
  if (gradeEl) {
@@ -32905,6 +33787,8 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
32905
33787
  gateEl.className = 'gate-badge gate-' + (data.gate_status || 'WARN').toLowerCase();
32906
33788
  }
32907
33789
  if (summaryEl) summaryEl.textContent = data.summary || '';
33790
+ if (scopeEl) scopeEl.textContent = formatHealthScope(data.scope);
33791
+ if (scopeProjectEl) scopeProjectEl.textContent = 'Project: ' + (data.project || '--');
32908
33792
 
32909
33793
  if (data.pillars) {
32910
33794
  const p1 = data.pillars.store_integrity || data.pillars.memory_integrity;
@@ -33007,7 +33891,7 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
33007
33891
 
33008
33892
  async function fetchDrift() {
33009
33893
  try {
33010
- const res = await fetch('/api/drift');
33894
+ const res = await fetch('/api/drift' + projectScopeQuery());
33011
33895
  const drift = await res.json();
33012
33896
  const tbody = document.getElementById('driftTableBody');
33013
33897
  if (tbody) {
@@ -33065,8 +33949,8 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
33065
33949
  async function fetchCognition() {
33066
33950
  try {
33067
33951
  const [clustRes, debtRes] = await Promise.all([
33068
- fetch('/api/cognition/clusters'),
33069
- fetch('/api/cognition/debt')
33952
+ fetch('/api/cognition/clusters' + projectScopeQuery()),
33953
+ fetch('/api/cognition/debt' + projectScopeQuery())
33070
33954
  ]);
33071
33955
  const clustersData = await clustRes.json();
33072
33956
  const debtData = await debtRes.json();
@@ -33303,6 +34187,16 @@ var MAX_BODY_SIZE, EMBEDDED_HTML = `<!DOCTYPE html>
33303
34187
  setupCanvasInteractions();
33304
34188
  setupTimeline();
33305
34189
 
34190
+ // T-321: project scope control. Seeded from the URL so a scoped link is
34191
+ // reloadable; an empty value means the whole store.
34192
+ const projectFilterInput = document.getElementById('projectFilter');
34193
+ if (projectFilterInput) {
34194
+ projectFilterInput.value = projectScope;
34195
+ projectFilterInput.addEventListener('change', () => {
34196
+ applyProjectScope(projectFilterInput.value);
34197
+ });
34198
+ }
34199
+
33306
34200
  let searchDebounceTimer = null;
33307
34201
  document.getElementById('searchInput').addEventListener('input', (e) => {
33308
34202
  searchQuery = e.target.value;
@@ -43347,7 +44241,7 @@ var package_default;
43347
44241
  var init_package = __esm(() => {
43348
44242
  package_default = {
43349
44243
  name: "musememory",
43350
- version: "3.0.2",
44244
+ version: "3.2.0",
43351
44245
  description: "Autonomous, self-organizing cognitive memory system for AI agents and agency networks",
43352
44246
  main: "dist/index.js",
43353
44247
  type: "module",
@@ -43368,13 +44262,17 @@ var init_package = __esm(() => {
43368
44262
  "build:gif": "bun run --cwd apps/website scripts/build-readme-gif.ts",
43369
44263
  "claims:generate": "bun scripts/generate-claims.ts && bun scripts/generate-cli-reference.ts",
43370
44264
  "claims:check": "bun scripts/generate-claims.ts --check",
44265
+ "check:landed": "bun run scripts/check-landed.ts",
44266
+ "check:landed:pr": "bun run scripts/check-landed.ts --pr",
43371
44267
  "docs:check": "bun scripts/check-doc-links.ts",
43372
44268
  test: "bun test",
43373
44269
  typecheck: "tsc --noEmit",
43374
44270
  validate: "bun run dist/index.js validate --dry-run",
43375
44271
  start: "bun run dist/index.js mcp",
43376
44272
  prepublishOnly: "bun run build && bun test",
43377
- "site:qa": "bun run --cwd apps/website scripts/qa-gate.ts"
44273
+ "site:qa": "bun run --cwd apps/website scripts/qa-gate.ts",
44274
+ "showreel:hash": "bun scripts/showreel-hash.ts",
44275
+ "showreel:hash:write": "bun scripts/showreel-hash.ts --write"
43378
44276
  },
43379
44277
  bin: {
43380
44278
  memory: "dist/index.js",
@@ -43410,6 +44308,9 @@ var init_package = __esm(() => {
43410
44308
  url: "git+ssh://git@github.com/harshsinghmp/musememory.git"
43411
44309
  },
43412
44310
  license: "MIT",
44311
+ publishConfig: {
44312
+ access: "public"
44313
+ },
43413
44314
  dependencies: {
43414
44315
  "@modelcontextprotocol/sdk": "^1.0.0",
43415
44316
  ajv: "^8.17.1",
@@ -44968,7 +45869,7 @@ var init_constitution = __esm(() => {
44968
45869
  trim_exempt: true,
44969
45870
  category: "runtime",
44970
45871
  severity: "critical",
44971
- source: "LifeOS Constitution R1"
45872
+ source: "Agency Architecture R1"
44972
45873
  },
44973
45874
  {
44974
45875
  id: "R2-zero-secret-exposure",
@@ -44977,7 +45878,7 @@ var init_constitution = __esm(() => {
44977
45878
  trim_exempt: true,
44978
45879
  category: "security",
44979
45880
  severity: "critical",
44980
- source: "LifeOS Vibeguard Protocol"
45881
+ source: "Vibeguard Security Protocol"
44981
45882
  },
44982
45883
  {
44983
45884
  id: "R3-evidence-before-claims",
@@ -45002,12 +45903,12 @@ var init_constitution = __esm(() => {
45002
45903
 
45003
45904
  // src/orchestrator/freshness.ts
45004
45905
  import { readFileSync as readFileSync49, statSync as statSync21 } from "node:fs";
45005
- import { resolve as resolve7 } from "node:path";
45906
+ import { resolve as resolve8 } from "node:path";
45006
45907
  function cacheKey2(fullPath, symbol, mtimeMs) {
45007
45908
  return `${fullPath}::${symbol ?? "*"}::${mtimeMs}`;
45008
45909
  }
45009
45910
  function checkAnchorLive(workspaceRoot, anchor) {
45010
- const fullPath = resolve7(workspaceRoot, anchor.file_path);
45911
+ const fullPath = resolve8(workspaceRoot, anchor.file_path);
45011
45912
  let mtimeMs = 0;
45012
45913
  try {
45013
45914
  mtimeMs = statSync21(fullPath).mtimeMs;
@@ -45553,7 +46454,8 @@ async function resolveMuseContext(store, workspaceRoot, input) {
45553
46454
  } catch {}
45554
46455
  }
45555
46456
  const allEntries = list(store);
45556
- const constraintEntries = allEntries.filter((e) => (e.type === "constraint" || e.temporal_mode === "timeless") && e.status !== "archived" && e.status !== "superseded" && e.status !== "rejected");
46457
+ const conflictedIds = new Set(allEntries.filter((e) => e.status === "conflicted").map((e) => e.id));
46458
+ const constraintEntries = allEntries.filter((e) => (e.type === "constraint" || e.temporal_mode === "timeless") && e.status !== "archived" && e.status !== "superseded" && e.status !== "rejected" && !(e.status === "conflicted" && !input.include_conflicted));
45557
46459
  for (const ce of constraintEntries) {
45558
46460
  if (activeConstraints.length >= 5)
45559
46461
  break;
@@ -45579,6 +46481,8 @@ ${c.content}`);
45579
46481
  continue;
45580
46482
  if (e.status === "archived" || e.status === "superseded")
45581
46483
  continue;
46484
+ if (e.status === "conflicted" && !input.include_conflicted)
46485
+ continue;
45582
46486
  for (const anc of e.anchors) {
45583
46487
  let fileMatch = false;
45584
46488
  let symbolMatch = false;
@@ -45608,6 +46512,7 @@ ${c.content}`);
45608
46512
  suppressReinjection: false,
45609
46513
  suppressSelfEcho: false,
45610
46514
  suppressSeenChunks: false,
46515
+ includeConflicted: input.include_conflicted === true,
45611
46516
  recordSeenChunks: false,
45612
46517
  echoHorizonMs: input.echo_horizon_ms,
45613
46518
  agent: input.agent,
@@ -45619,6 +46524,13 @@ ${c.content}`);
45619
46524
  active_file: input.active_file,
45620
46525
  symbol: input.symbol
45621
46526
  });
46527
+ if (!input.include_conflicted && conflictedIds.size > 0) {
46528
+ routeLookup.matched_routes = routeLookup.matched_routes.map((rt) => ({
46529
+ ...rt,
46530
+ entry_ids: rt.entry_ids.filter((id) => !conflictedIds.has(id))
46531
+ }));
46532
+ routeLookup.prioritized_entry_ids = routeLookup.prioritized_entry_ids.filter((id) => !conflictedIds.has(id));
46533
+ }
45622
46534
  const candidatePool = trivialGate.isTrivial ? [] : [...entriesWithMatchingAnchors];
45623
46535
  if (!trivialGate.isTrivial) {
45624
46536
  for (const pid of routeLookup.prioritized_entry_ids) {
@@ -45635,6 +46547,18 @@ ${c.content}`);
45635
46547
  }
45636
46548
  }
45637
46549
  }
46550
+ const withheldConflicted = [];
46551
+ if (!input.include_conflicted && conflictedIds.size > 0) {
46552
+ for (let i = candidatePool.length - 1;i >= 0; i--) {
46553
+ if (conflictedIds.has(candidatePool[i].id)) {
46554
+ const removed = candidatePool.splice(i, 1)[0];
46555
+ withheldConflicted.push({
46556
+ id: removed.id,
46557
+ reason: "conflicted_status - resolve in memory conflicts; opt-in via include_conflicted: true"
46558
+ });
46559
+ }
46560
+ }
46561
+ }
45638
46562
  const withheldUntrusted = [];
45639
46563
  let filteredCandidates = candidatePool;
45640
46564
  if (!input.include_untrusted) {
@@ -45801,6 +46725,9 @@ ${c.content}`);
45801
46725
  if (withheldUntrusted.length > 0) {
45802
46726
  suggestedNextSteps.push(`${withheldUntrusted.length} untrusted memor${withheldUntrusted.length === 1 ? "y" : "ies"} withheld — pass include_untrusted: true to include them`);
45803
46727
  }
46728
+ if (withheldConflicted.length > 0) {
46729
+ suggestedNextSteps.push(`${withheldConflicted.length} conflicted memor${withheldConflicted.length === 1 ? "y" : "ies"} withheld — resolve them via memory conflicts or pass include_conflicted: true`);
46730
+ }
45804
46731
  if (digestRefusalReason) {
45805
46732
  suggestedNextSteps.push(`Refused incomplete or corrupted CURRENT.md digest: ${digestRefusalReason} — re-generate with memory handoff or repair CURRENT.md`);
45806
46733
  }
@@ -46536,6 +47463,7 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
46536
47463
  limit: { type: "number" },
46537
47464
  token_budget: { type: "number", description: "Maximum token budget to consume" },
46538
47465
  include_superseded: { type: "boolean" },
47466
+ include_conflicted: { type: "boolean", description: "Include memories flagged conflicted by the contradiction engine (excluded by default — known potential falsehoods)" },
46539
47467
  hybrid: { type: "boolean", description: "Use the offline vector+BM25 index (requires 'memory reindex'); falls back to live scoring when absent" },
46540
47468
  type: { type: "string" },
46541
47469
  status: { type: "string" },
@@ -46656,6 +47584,7 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
46656
47584
  limit: { type: "number" },
46657
47585
  token_budget: { type: "number", description: "Maximum token budget to consume" },
46658
47586
  include_superseded: { type: "boolean" },
47587
+ include_conflicted: { type: "boolean", description: "Include memories flagged conflicted by the contradiction engine (excluded by default — known potential falsehoods)" },
46659
47588
  type: { type: "string" },
46660
47589
  status: { type: "string" },
46661
47590
  verified: { type: "boolean" },
@@ -47754,7 +48683,8 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
47754
48683
  inputSchema: {
47755
48684
  type: "object",
47756
48685
  properties: {
47757
- dir: { type: "string", description: "Optional project workspace directory path" }
48686
+ dir: { type: "string", description: "Optional project workspace directory path" },
48687
+ project: { type: "string", description: "Optional memory project scope filter" }
47758
48688
  }
47759
48689
  }
47760
48690
  },
@@ -47890,7 +48820,8 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
47890
48820
  inputSchema: {
47891
48821
  type: "object",
47892
48822
  properties: {
47893
- dir: { type: "string", description: "Optional project workspace directory path" }
48823
+ dir: { type: "string", description: "Optional project workspace directory path" },
48824
+ project: { type: "string", description: "Optional memory project scope filter" }
47894
48825
  }
47895
48826
  }
47896
48827
  },
@@ -47914,7 +48845,8 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
47914
48845
  inputSchema: {
47915
48846
  type: "object",
47916
48847
  properties: {
47917
- dir: { type: "string", description: "Optional workspace directory" }
48848
+ dir: { type: "string", description: "Optional workspace directory" },
48849
+ project: { type: "string", description: "Optional memory project scope filter" }
47918
48850
  }
47919
48851
  }
47920
48852
  },
@@ -47924,7 +48856,8 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
47924
48856
  inputSchema: {
47925
48857
  type: "object",
47926
48858
  properties: {
47927
- dir: { type: "string", description: "Optional workspace directory" }
48859
+ dir: { type: "string", description: "Optional workspace directory" },
48860
+ project: { type: "string", description: "Optional memory project scope filter" }
47928
48861
  }
47929
48862
  }
47930
48863
  },
@@ -47934,7 +48867,8 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
47934
48867
  inputSchema: {
47935
48868
  type: "object",
47936
48869
  properties: {
47937
- dir: { type: "string", description: "Optional workspace directory path" }
48870
+ dir: { type: "string", description: "Optional workspace directory path" },
48871
+ project: { type: "string", description: "Optional memory project scope filter" }
47938
48872
  }
47939
48873
  }
47940
48874
  },
@@ -48241,6 +49175,7 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
48241
49175
  limit: clampLimit(a.limit, 10, 100, 1),
48242
49176
  tokenBudget: typeof a.token_budget === "number" ? a.token_budget : undefined,
48243
49177
  includeSuperseded: a.include_superseded === true,
49178
+ includeConflicted: a.include_conflicted === true,
48244
49179
  type: a.type ? String(a.type) : undefined,
48245
49180
  status: a.status ? String(a.status) : undefined,
48246
49181
  verified: a.verified === true,
@@ -49144,7 +50079,14 @@ Path: ${item.path}`,
49144
50079
  });
49145
50080
  }
49146
50081
  case "memory_anchor_audit": {
49147
- const report = auditMemoryAnchors(activeStore, activeRoot);
50082
+ const requestedProject = a.project ? String(a.project).trim() || undefined : undefined;
50083
+ const canonicalProject = resolveProjectName(activeStore, requestedProject);
50084
+ if (requestedProject && canonicalProject === undefined) {
50085
+ return toolError(`no memories for project "${requestedProject}" — available: ${formatAvailable(activeStore)}`);
50086
+ }
50087
+ const report = auditMemoryAnchors(activeStore, activeRoot, {
50088
+ project: canonicalProject
50089
+ });
49148
50090
  return toolResult(report);
49149
50091
  }
49150
50092
  case "muse_context": {
@@ -49226,7 +50168,14 @@ Path: ${item.path}`,
49226
50168
  });
49227
50169
  }
49228
50170
  case "memory_drift_audit": {
49229
- const report = detectDocumentationCodeDrift(activeStore, activeRoot);
50171
+ const requestedProject = a.project ? String(a.project).trim() || undefined : undefined;
50172
+ const canonicalProject = resolveProjectName(activeStore, requestedProject);
50173
+ if (requestedProject && canonicalProject === undefined) {
50174
+ return toolError(`no memories for project "${requestedProject}" — available: ${formatAvailable(activeStore)}`);
50175
+ }
50176
+ const report = detectDocumentationCodeDrift(activeStore, activeRoot, {
50177
+ project: canonicalProject
50178
+ });
49230
50179
  return toolResult(report);
49231
50180
  }
49232
50181
  case "muse_why": {
@@ -49238,18 +50187,39 @@ Path: ${item.path}`,
49238
50187
  return toolResult(explanation);
49239
50188
  }
49240
50189
  case "muse_bug_clusters": {
49241
- const clusters = clusterRecurringBugsAndFriction(activeStore);
50190
+ const requestedProject = a.project ? String(a.project).trim() || undefined : undefined;
50191
+ const canonicalProject = resolveProjectName(activeStore, requestedProject);
50192
+ if (requestedProject && canonicalProject === undefined) {
50193
+ return toolError(`no memories for project "${requestedProject}" — available: ${formatAvailable(activeStore)}`);
50194
+ }
50195
+ const clusters = clusterRecurringBugsAndFriction(activeStore, {
50196
+ project: canonicalProject
50197
+ });
49242
50198
  return toolResult({
49243
50199
  total_clusters: clusters.length,
49244
50200
  clusters
49245
50201
  });
49246
50202
  }
49247
50203
  case "muse_tech_debt": {
49248
- const report = analyzeTechnicalDebt(activeStore, activeRoot);
50204
+ const requestedProject = a.project ? String(a.project).trim() || undefined : undefined;
50205
+ const canonicalProject = resolveProjectName(activeStore, requestedProject);
50206
+ if (requestedProject && canonicalProject === undefined) {
50207
+ return toolError(`no memories for project "${requestedProject}" — available: ${formatAvailable(activeStore)}`);
50208
+ }
50209
+ const report = analyzeTechnicalDebt(activeStore, activeRoot, {
50210
+ project: canonicalProject
50211
+ });
49249
50212
  return toolResult(report);
49250
50213
  }
49251
50214
  case "muse_health": {
49252
- const report = evaluateProjectHealth(activeStore, activeRoot);
50215
+ const requestedProject = a.project ? String(a.project).trim() || undefined : undefined;
50216
+ const canonicalProject = resolveProjectName(activeStore, requestedProject);
50217
+ if (requestedProject && canonicalProject === undefined) {
50218
+ return toolError(`no memories for project "${requestedProject}" — available: ${formatAvailable(activeStore)}`);
50219
+ }
50220
+ const report = evaluateProjectHealth(activeStore, activeRoot, {
50221
+ project: canonicalProject
50222
+ });
49253
50223
  return toolResult(report);
49254
50224
  }
49255
50225
  case "muse_sync_broadcast": {
@@ -50641,6 +51611,7 @@ async function handleSearchCommand({ positional, flags }) {
50641
51611
  tokenBudget,
50642
51612
  project: flags["project"],
50643
51613
  includeSuperseded: flags["include-superseded"] === "true",
51614
+ includeConflicted: flags["include-conflicted"] === "true",
50644
51615
  type: flags["type"],
50645
51616
  status: flags["status"],
50646
51617
  verified: flags["verified"] === "true"
@@ -52544,7 +53515,17 @@ async function handleHealthCommand(parsed) {
52544
53515
  return 1;
52545
53516
  }
52546
53517
  const { root, store } = ctx;
52547
- const report = evaluateProjectHealth(store, root);
53518
+ const requestedProject = parsed.flags["project"]?.trim() || undefined;
53519
+ const canonicalProject = resolveProjectName(store, requestedProject);
53520
+ if (requestedProject && canonicalProject === undefined) {
53521
+ console.error(`Error: no memories for project "${requestedProject}" — available: ${formatAvailable(store)}`);
53522
+ return 1;
53523
+ }
53524
+ const project = canonicalProject;
53525
+ if (canonicalProject && canonicalProject !== requestedProject) {
53526
+ console.error(`Note: project filter matched canonical "${canonicalProject}" (requested "${requestedProject}").`);
53527
+ }
53528
+ const report = evaluateProjectHealth(store, root, { project });
52548
53529
  if (parsed.flags.json) {
52549
53530
  console.log(JSON.stringify(report, null, 2));
52550
53531
  return report.gate_status === "FAIL" ? 1 : 0;
@@ -52556,6 +53537,7 @@ async function handleHealthCommand(parsed) {
52556
53537
  ${bold}\uD83C\uDFE5 MUSE MEMORY 5-PILLAR PROJECT HEALTH GATE${reset}`);
52557
53538
  console.log(`${"=".repeat(50)}`);
52558
53539
  console.log(`Project: ${bold}${report.project}${reset}`);
53540
+ console.log(`Scope: ${report.scope.entry_count} ` + `${report.scope.entry_count === 1 ? "memory" : "memories"} across ` + `${report.scope.projects.length} project${report.scope.projects.length === 1 ? "" : "s"}` + (report.scope.filter ? ` (filtered to "${report.scope.filter}")` : ""));
52559
53541
  console.log(`Overall Grade: ${bold}${report.overall_grade}${reset} (${report.overall_score}/100)`);
52560
53542
  console.log(`Gate Status: ${statusColor}${bold}${report.gate_status}${reset}
52561
53543
  `);
@@ -52704,9 +53686,98 @@ var init_health2 = __esm(() => {
52704
53686
  init_shared();
52705
53687
  });
52706
53688
 
53689
+ // src/cli/conflicts.ts
53690
+ async function handleConflictsCommand(parsed) {
53691
+ const ctx = requireRoot(parsed.flags);
53692
+ if (!ctx) {
53693
+ console.error("Error: Could not resolve project root or initialize .memory directory.");
53694
+ return 1;
53695
+ }
53696
+ const { store } = ctx;
53697
+ const isTrue = (val) => val === true || val === "true" || val === "";
53698
+ const dryRun = isTrue(parsed.flags["dry-run"]) || isTrue(parsed.flags["n"]);
53699
+ const project = parsed.flags["project"];
53700
+ await Promise.resolve().then(() => init_contradiction());
53701
+ const recorded = listConflicts(store, { status: "open" });
53702
+ const resolved = listConflicts(store, { status: "resolved" });
53703
+ if (dryRun) {
53704
+ const potential = scanPotentialConflicts(store, { project });
53705
+ if (parsed.flags.json) {
53706
+ console.log(JSON.stringify({
53707
+ mode: "dry-run",
53708
+ project: project ?? null,
53709
+ recorded_open: recorded.length,
53710
+ recorded_resolved: resolved.length,
53711
+ potential_conflicts: potential
53712
+ }, null, 2));
53713
+ return 0;
53714
+ }
53715
+ const bold = "\x1B[1m";
53716
+ const reset = "\x1B[0m";
53717
+ const yellow = "\x1B[33m";
53718
+ const red = "\x1B[31m";
53719
+ console.log(`
53720
+ ${bold}\uD83D\uDD0D CONTRADICTION ENGINE — DRY RUN${reset}`);
53721
+ console.log(`${"=".repeat(56)}`);
53722
+ console.log(`Project: ${bold}${project ?? "all"}${reset}`);
53723
+ console.log(`Recorded (open): ${recorded.length}`);
53724
+ console.log(`Recorded (resolved):${resolved.length}`);
53725
+ console.log(`Potential pairs: ${potential.length}
53726
+ `);
53727
+ if (potential.length === 0) {
53728
+ console.log(` ✅ No memory pairs would be flagged as contradictory.
53729
+ `);
53730
+ return 0;
53731
+ }
53732
+ console.log(`${yellow}Would be flagged (nothing was changed):${reset}`);
53733
+ for (const pair of potential) {
53734
+ console.log(` ${red}✗${reset} ${pair.entry_a_title}`);
53735
+ console.log(` ${pair.entry_a_id}`);
53736
+ console.log(` ${red}✗${reset} ${pair.entry_b_title}`);
53737
+ console.log(` ${pair.entry_b_id}`);
53738
+ console.log(` reason: ${pair.reason ?? "unknown"}`);
53739
+ console.log("");
53740
+ }
53741
+ console.log(`${bold}Note${reset}: flagging a pair moves BOTH memories to 'conflicted' (25 health points each)
53742
+ and removes both from every future sync broadcast. Resolve with 'memory_resolve_conflict'.
53743
+ `);
53744
+ return 0;
53745
+ }
53746
+ if (parsed.flags.json) {
53747
+ console.log(JSON.stringify({ mode: "recorded", open: recorded, resolved: resolved.length }, null, 2));
53748
+ return 0;
53749
+ }
53750
+ const bold = "\x1B[1m";
53751
+ const reset = "\x1B[0m";
53752
+ console.log(`
53753
+ ${bold}⚖️ CONFLICTS${reset}`);
53754
+ console.log(`${"=".repeat(56)}`);
53755
+ console.log(`Open: ${recorded.length}`);
53756
+ console.log(`Resolved: ${resolved.length}
53757
+ `);
53758
+ if (recorded.length === 0) {
53759
+ console.log(` ✅ No open conflicts.
53760
+ `);
53761
+ return 0;
53762
+ }
53763
+ for (const conflict of recorded) {
53764
+ console.log(`${bold}${conflict.memory_a_id}${reset} ↔ ${bold}${conflict.memory_b_id}${reset}`);
53765
+ console.log(` type: ${conflict.conflict_type} confidence: ${conflict.confidence}`);
53766
+ console.log(` reason: ${conflict.reason ?? "unknown"}`);
53767
+ console.log(` detected: ${conflict.detected_at}`);
53768
+ console.log("");
53769
+ }
53770
+ console.log(`${bold}Tip${reset}: run 'memory conflicts --dry-run' to preview what would be flagged.
53771
+ `);
53772
+ return 0;
53773
+ }
53774
+ var init_conflicts = __esm(() => {
53775
+ init_shared();
53776
+ });
53777
+
52707
53778
  // src/cli/sync.ts
52708
53779
  import { writeFileSync as writeFileSync39, readFileSync as readFileSync59 } from "node:fs";
52709
- import { resolve as resolve8 } from "node:path";
53780
+ import { resolve as resolve9 } from "node:path";
52710
53781
  async function handleSyncCommand(parsed) {
52711
53782
  const ctx = requireRoot(parsed.flags);
52712
53783
  if (!ctx) {
@@ -52721,7 +53792,7 @@ async function handleSyncCommand(parsed) {
52721
53792
  project: parsed.flags.project
52722
53793
  });
52723
53794
  if (parsed.flags.out) {
52724
- const outPath = resolve8(process.cwd(), parsed.flags.out);
53795
+ const outPath = resolve9(process.cwd(), parsed.flags.out);
52725
53796
  writeFileSync39(outPath, JSON.stringify(packet, null, 2), "utf-8");
52726
53797
  console.log(`\x1B[32m✓ Broadcast packet saved to '${outPath}' (${packet.payload.memories.length} memories)\x1B[0m`);
52727
53798
  } else {
@@ -52730,7 +53801,7 @@ async function handleSyncCommand(parsed) {
52730
53801
  return 0;
52731
53802
  }
52732
53803
  if (parsed.flags.ingest) {
52733
- const inPath = resolve8(process.cwd(), parsed.flags.ingest);
53804
+ const inPath = resolve9(process.cwd(), parsed.flags.ingest);
52734
53805
  try {
52735
53806
  const raw = readFileSync59(inPath, "utf-8");
52736
53807
  const packet = JSON.parse(raw);
@@ -52755,7 +53826,8 @@ async function handleSyncCommand(parsed) {
52755
53826
  }
52756
53827
  }
52757
53828
  if (parsed.flags.pool || !parsed.flags.status && !parsed.flags.broadcast && !parsed.flags.ingest) {
52758
- const poolDir = typeof parsed.flags.pool === "string" ? resolve8(process.cwd(), parsed.flags.pool) : undefined;
53829
+ const rawPool = parsed.flags.pool;
53830
+ const poolDir = typeof rawPool === "string" && rawPool !== "true" && rawPool.trim() !== "" ? resolve9(process.cwd(), rawPool) : undefined;
52759
53831
  const report = syncWithSharedPool(store, root, poolDir, agentId);
52760
53832
  if (parsed.flags.json) {
52761
53833
  console.log(JSON.stringify(report, null, 2));
@@ -53112,6 +54184,394 @@ var init_versioning = __esm(() => {
53112
54184
  init_retention();
53113
54185
  });
53114
54186
 
54187
+ // src/cli/secrets.ts
54188
+ import { existsSync as existsSync69, readdirSync as readdirSync23, readFileSync as readFileSync61, statSync as statSync22 } from "node:fs";
54189
+ import { join as join64, relative as relative10, extname as extname2 } from "node:path";
54190
+ function isBinaryBuffer(buffer) {
54191
+ const checkLength = Math.min(buffer.length, 1024);
54192
+ for (let i = 0;i < checkLength; i++) {
54193
+ if (buffer[i] === 0)
54194
+ return true;
54195
+ }
54196
+ return false;
54197
+ }
54198
+ function walkScanFiles(dir, baseDir = dir, customIgnores = []) {
54199
+ const results = [];
54200
+ if (!existsSync69(dir))
54201
+ return results;
54202
+ const entries = readdirSync23(dir, { withFileTypes: true });
54203
+ for (const entry of entries) {
54204
+ const fullPath = join64(dir, entry.name);
54205
+ const relPath = relative10(baseDir, fullPath);
54206
+ if (entry.isDirectory()) {
54207
+ if (DEFAULT_IGNORED_DIRS.has(entry.name) || customIgnores.some((pattern) => relPath.includes(pattern))) {
54208
+ continue;
54209
+ }
54210
+ results.push(...walkScanFiles(fullPath, baseDir, customIgnores));
54211
+ } else if (entry.isFile()) {
54212
+ const ext = extname2(entry.name).toLowerCase();
54213
+ if (BINARY_EXTENSIONS.has(ext))
54214
+ continue;
54215
+ if (customIgnores.some((pattern) => relPath.includes(pattern)))
54216
+ continue;
54217
+ results.push(fullPath);
54218
+ }
54219
+ }
54220
+ return results;
54221
+ }
54222
+ async function executeSecretScan(options) {
54223
+ const startTime = Date.now();
54224
+ const targetDir = options.targetDir;
54225
+ const customIgnores = options.ignorePatterns || [];
54226
+ if (!existsSync69(targetDir)) {
54227
+ return {
54228
+ targetDir,
54229
+ scannedFiles: 0,
54230
+ findings: [],
54231
+ clean: false,
54232
+ durationMs: Date.now() - startTime
54233
+ };
54234
+ }
54235
+ let files = [];
54236
+ try {
54237
+ const stat = statSync22(targetDir);
54238
+ if (stat.isFile()) {
54239
+ files = [targetDir];
54240
+ } else {
54241
+ files = walkScanFiles(targetDir, targetDir, customIgnores);
54242
+ }
54243
+ } catch {
54244
+ files = [];
54245
+ }
54246
+ const findings = [];
54247
+ for (const file of files) {
54248
+ try {
54249
+ const stats = statSync22(file);
54250
+ if (stats.size > 5 * 1024 * 1024)
54251
+ continue;
54252
+ const buffer = readFileSync61(file);
54253
+ if (isBinaryBuffer(buffer))
54254
+ continue;
54255
+ const content = buffer.toString("utf8");
54256
+ const lines = content.split(/\r?\n/);
54257
+ for (let i = 0;i < lines.length; i++) {
54258
+ const line = lines[i];
54259
+ if (!line.trim())
54260
+ continue;
54261
+ for (const rule of SECRET_RULES) {
54262
+ if (rule.pattern.test(line)) {
54263
+ findings.push({
54264
+ file: relative10(targetDir, file) || file,
54265
+ line: i + 1,
54266
+ rule: rule.name,
54267
+ redactedSnippet: redactSecrets(line.trim())
54268
+ });
54269
+ break;
54270
+ }
54271
+ }
54272
+ }
54273
+ } catch {}
54274
+ }
54275
+ const durationMs = Date.now() - startTime;
54276
+ return {
54277
+ targetDir,
54278
+ scannedFiles: files.length,
54279
+ findings,
54280
+ clean: findings.length === 0,
54281
+ durationMs
54282
+ };
54283
+ }
54284
+ async function handleScanSecretsCommand(args) {
54285
+ const targetDir = args.positional?.[0] || args.args?.[0] || ".";
54286
+ const verbose = Boolean(args.flags.verbose || args.flags.v);
54287
+ const jsonMode = Boolean(args.flags.json);
54288
+ const silent = Boolean(args.flags.silent);
54289
+ const failOnSecrets = args.flags["fail-on-secrets"] !== "false";
54290
+ const customIgnores = args.flags.ignore ? String(args.flags.ignore).split(",").map((s) => s.trim()) : [];
54291
+ if (!existsSync69(targetDir)) {
54292
+ if (!silent) {
54293
+ console.error(`
54294
+ ❌ Error: Target path does not exist: ${targetDir}`);
54295
+ }
54296
+ return 1;
54297
+ }
54298
+ const result = await executeSecretScan({
54299
+ targetDir,
54300
+ verbose,
54301
+ json: jsonMode,
54302
+ silent,
54303
+ ignorePatterns: customIgnores,
54304
+ failOnSecrets
54305
+ });
54306
+ if (jsonMode) {
54307
+ console.log(JSON.stringify(result, null, 2));
54308
+ return result.clean ? 0 : 1;
54309
+ }
54310
+ if (silent) {
54311
+ return result.clean ? 0 : 1;
54312
+ }
54313
+ console.log(`
54314
+ \uD83D\uDEE1️ Native Vibeguard Secret Scanner`);
54315
+ console.log(`─────────────────────────────────────────────────────────────`);
54316
+ console.log(`Target Directory : ${targetDir}`);
54317
+ console.log(`Scanned Files : ${result.scannedFiles}`);
54318
+ console.log(`Duration : ${result.durationMs}ms`);
54319
+ if (result.clean) {
54320
+ console.log(`
54321
+ ✅ Zero credentials leaked. All files verified clean.`);
54322
+ return 0;
54323
+ }
54324
+ console.log(`
54325
+ \uD83D\uDEA8 Potential Credential Leaks Detected: ${result.findings.length}
54326
+ `);
54327
+ for (const finding of result.findings) {
54328
+ console.log(` \uD83D\uDCCD ${finding.file}:${finding.line}`);
54329
+ console.log(` Rule : ${finding.rule}`);
54330
+ console.log(` Preview : ${finding.redactedSnippet}`);
54331
+ console.log(``);
54332
+ }
54333
+ console.log(`❌ Audit failed. Remove or redact exposed secrets before committing.`);
54334
+ return failOnSecrets ? 1 : 0;
54335
+ }
54336
+ var DEFAULT_IGNORED_DIRS, BINARY_EXTENSIONS;
54337
+ var init_secrets2 = __esm(() => {
54338
+ init_secrets();
54339
+ DEFAULT_IGNORED_DIRS = new Set([
54340
+ ".git",
54341
+ "node_modules",
54342
+ "dist",
54343
+ "build",
54344
+ ".next",
54345
+ ".astro",
54346
+ "coverage",
54347
+ ".turbo",
54348
+ ".cache",
54349
+ ".agents/archive",
54350
+ "snapshots"
54351
+ ]);
54352
+ BINARY_EXTENSIONS = new Set([
54353
+ ".png",
54354
+ ".jpg",
54355
+ ".jpeg",
54356
+ ".gif",
54357
+ ".webp",
54358
+ ".ico",
54359
+ ".woff",
54360
+ ".woff2",
54361
+ ".ttf",
54362
+ ".eot",
54363
+ ".otf",
54364
+ ".mp4",
54365
+ ".webm",
54366
+ ".mp3",
54367
+ ".wav",
54368
+ ".zip",
54369
+ ".tar",
54370
+ ".gz",
54371
+ ".tgz",
54372
+ ".7z",
54373
+ ".rar",
54374
+ ".sqlite",
54375
+ ".db",
54376
+ ".bin",
54377
+ ".exe",
54378
+ ".dll",
54379
+ ".so",
54380
+ ".dylib",
54381
+ ".wasm",
54382
+ ".lock",
54383
+ ".lockb",
54384
+ ".pdf"
54385
+ ]);
54386
+ });
54387
+
54388
+ // src/vault/system.ts
54389
+ import { existsSync as existsSync70, mkdirSync as mkdirSync36, writeFileSync as writeFileSync41 } from "node:fs";
54390
+ import { join as join65 } from "node:path";
54391
+ import os from "node:os";
54392
+ import { Socket } from "node:net";
54393
+ import { spawnSync } from "node:child_process";
54394
+ function probePort(port) {
54395
+ return new Promise((resolve) => {
54396
+ const socket = new Socket;
54397
+ socket.setTimeout(100);
54398
+ socket.once("connect", () => {
54399
+ socket.destroy();
54400
+ resolve(true);
54401
+ });
54402
+ socket.once("timeout", () => {
54403
+ socket.destroy();
54404
+ resolve(false);
54405
+ });
54406
+ socket.once("error", () => {
54407
+ socket.destroy();
54408
+ resolve(false);
54409
+ });
54410
+ socket.connect(port, "127.0.0.1");
54411
+ });
54412
+ }
54413
+ function runBinarySafe(binary, args) {
54414
+ try {
54415
+ const res = spawnSync(binary, args, { stdio: ["ignore", "pipe", "ignore"], timeout: 1500 });
54416
+ if (res.status === 0 && res.stdout) {
54417
+ return res.stdout.toString("utf8").trim();
54418
+ }
54419
+ } catch {
54420
+ return;
54421
+ }
54422
+ return;
54423
+ }
54424
+ async function collectSystemTerrain() {
54425
+ const cpus = os.cpus() || [];
54426
+ const firstCpu = cpus[0] || { model: "Unknown", speed: 0 };
54427
+ const host = {
54428
+ platform: os.platform(),
54429
+ arch: os.arch(),
54430
+ hostname: os.hostname(),
54431
+ release: os.release(),
54432
+ cpus: {
54433
+ model: firstCpu.model,
54434
+ speed: firstCpu.speed,
54435
+ count: cpus.length
54436
+ },
54437
+ totalMemoryGb: Math.round(os.totalmem() / 1024 / 1024 / 1024 * 100) / 100,
54438
+ freeMemoryGb: Math.round(os.freemem() / 1024 / 1024 / 1024 * 100) / 100,
54439
+ uptimeHours: Math.round(os.uptime() / 3600 * 10) / 10
54440
+ };
54441
+ const toolchains = {
54442
+ bunVersion: process.versions.bun || runBinarySafe("bun", ["--version"]),
54443
+ nodeVersion: process.versions.node || runBinarySafe("node", ["--version"]),
54444
+ gitVersion: runBinarySafe("git", ["--version"])?.replace(/^git version\s*/, ""),
54445
+ cargoVersion: runBinarySafe("cargo", ["--version"])?.replace(/^cargo\s*/, ""),
54446
+ pythonVersion: runBinarySafe("python3", ["--version"])?.replace(/^Python\s*/, ""),
54447
+ dockerAvailable: Boolean(runBinarySafe("docker", ["--version"]))
54448
+ };
54449
+ const knownServices = [
54450
+ { port: 2222, name: "Muse Memory Studio" },
54451
+ { port: 31337, name: "Pulse Dashboard" },
54452
+ { port: 3600, name: "Ryoku Rashin Hub" },
54453
+ { port: 7878, name: "Muse Daemon SSE" },
54454
+ { port: 11434, name: "Ollama Local LLM" },
54455
+ { port: 8000, name: "Local AI Endpoint" }
54456
+ ];
54457
+ const probedPorts = await Promise.all(knownServices.map(async (svc) => {
54458
+ const listening = await probePort(svc.port);
54459
+ return { port: svc.port, service: svc.name, listening };
54460
+ }));
54461
+ let installedAgents = [];
54462
+ try {
54463
+ const detected = detectAgents();
54464
+ installedAgents = detected.filter((a) => a.installed).map((a) => a.name);
54465
+ } catch {
54466
+ installedAgents = ["antigravity", "opencode", "claude-code"];
54467
+ }
54468
+ return {
54469
+ timestamp: new Date().toISOString(),
54470
+ host,
54471
+ toolchains,
54472
+ agentEcosystem: {
54473
+ activePorts: probedPorts.filter((p) => p.listening),
54474
+ detectedAgents: installedAgents
54475
+ }
54476
+ };
54477
+ }
54478
+ function formatSystemTerrainMarkdown(terrain) {
54479
+ const { host, toolchains, agentEcosystem } = terrain;
54480
+ const lines = [
54481
+ `# Machine & System Terrain`,
54482
+ `> Deterministic hardware, runtime toolchains, and agent ecosystem map.`,
54483
+ `> Collected at: \`${terrain.timestamp}\``,
54484
+ ``,
54485
+ `## \uD83D\uDDA5️ Host & Hardware`,
54486
+ `| Property | Value |`,
54487
+ `| --- | --- |`,
54488
+ `| **OS / Platform** | \`${host.platform}\` (${host.release}) |`,
54489
+ `| **Architecture** | \`${host.arch}\` |`,
54490
+ `| **Hostname** | \`${host.hostname}\` |`,
54491
+ `| **CPU Model** | ${host.cpus.model} |`,
54492
+ `| **CPU Cores** | ${host.cpus.count} cores @ ${host.cpus.speed} MHz |`,
54493
+ `| **Memory (RAM)** | ${host.freeMemoryGb} GB free / ${host.totalMemoryGb} GB total |`,
54494
+ `| **Host Uptime** | ${host.uptimeHours} hours |`,
54495
+ ``,
54496
+ `## \uD83D\uDEE0️ Toolchains & Runtimes`,
54497
+ `| Runtime / Tool | Version / Status |`,
54498
+ `| --- | --- |`,
54499
+ `| **Bun Runtime** | \`${toolchains.bunVersion || "Not Found"}\` |`,
54500
+ `| **Node.js** | \`${toolchains.nodeVersion || "Not Found"}\` |`,
54501
+ `| **Git CLI** | \`${toolchains.gitVersion || "Not Found"}\` |`,
54502
+ `| **Cargo (Rust)** | \`${toolchains.cargoVersion || "Not Found"}\` |`,
54503
+ `| **Python 3** | \`${toolchains.pythonVersion || "Not Found"}\` |`,
54504
+ `| **Docker Engine** | \`${toolchains.dockerAvailable ? "Available" : "Unavailable"}\` |`,
54505
+ ``,
54506
+ `## \uD83E\uDD16 Agent Ecosystem`,
54507
+ `### Installed Coding Agents (${agentEcosystem.detectedAgents.length})`,
54508
+ agentEcosystem.detectedAgents.length > 0 ? agentEcosystem.detectedAgents.map((a) => `- \`${a}\``).join(`
54509
+ `) : `_No recognized agent platforms installed._`,
54510
+ ``,
54511
+ `### Active Local Agent Ports`
54512
+ ];
54513
+ if (agentEcosystem.activePorts.length > 0) {
54514
+ lines.push(`| Port | Service | Status |`);
54515
+ lines.push(`| --- | --- | --- |`);
54516
+ for (const p of agentEcosystem.activePorts) {
54517
+ lines.push(`| \`:${p.port}\` | ${p.service} | \uD83D\uDFE2 Active |`);
54518
+ }
54519
+ } else {
54520
+ lines.push(`_No common agent daemons actively listening._`);
54521
+ }
54522
+ lines.push(``);
54523
+ lines.push(`---`);
54524
+ lines.push(`*Generated by Muse Vault System Collector.*`);
54525
+ return lines.join(`
54526
+ `);
54527
+ }
54528
+ async function writeSystemTerrainMap(targetVaultDir) {
54529
+ if (!existsSync70(targetVaultDir)) {
54530
+ mkdirSync36(targetVaultDir, { recursive: true });
54531
+ }
54532
+ const terrain = await collectSystemTerrain();
54533
+ const md = formatSystemTerrainMarkdown(terrain);
54534
+ const targetFile = join65(targetVaultDir, "system.md");
54535
+ writeFileSync41(targetFile, md, "utf8");
54536
+ return targetFile;
54537
+ }
54538
+ async function handleTerrainCommand(args) {
54539
+ const silent = Boolean(args.flags.silent);
54540
+ const outFile = args.flags.out;
54541
+ const projectRootInfo = findOrCreateProjectRoot(process.cwd());
54542
+ const defaultVaultDir = join65(projectRootInfo.memoryDir, "vault");
54543
+ const terrain = await collectSystemTerrain();
54544
+ const markdown = formatSystemTerrainMarkdown(terrain);
54545
+ if (outFile) {
54546
+ writeFileSync41(outFile, markdown, "utf8");
54547
+ if (!silent) {
54548
+ console.log(`
54549
+ \uD83D\uDDFA️ Machine terrain written to: ${outFile}`);
54550
+ }
54551
+ return 0;
54552
+ }
54553
+ try {
54554
+ const savedPath = await writeSystemTerrainMap(defaultVaultDir);
54555
+ if (!silent) {
54556
+ console.log(`
54557
+ \uD83D\uDDFA️ Machine terrain compiled to: ${savedPath}
54558
+ `);
54559
+ console.log(markdown);
54560
+ }
54561
+ return 0;
54562
+ } catch (err) {
54563
+ if (!silent) {
54564
+ console.error(`
54565
+ ❌ Failed to write terrain map: ${err.message}`);
54566
+ }
54567
+ return 1;
54568
+ }
54569
+ }
54570
+ var init_system = __esm(() => {
54571
+ init_detect2();
54572
+ init_root();
54573
+ });
54574
+
53115
54575
  // src/cli.ts
53116
54576
  async function main(argv) {
53117
54577
  if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
@@ -53276,10 +54736,19 @@ async function main(argv) {
53276
54736
  return handleLoopCommand(parsed);
53277
54737
  case "health":
53278
54738
  return handleHealthCommand(parsed);
54739
+ case "conflicts":
54740
+ return handleConflictsCommand(parsed);
53279
54741
  case "reconcile":
53280
54742
  return handleReconcileCommand(parsed);
53281
54743
  case "benchmark":
53282
54744
  return handleBenchmarkCommand(parsed);
54745
+ case "scan-secrets":
54746
+ case "secrets-scan":
54747
+ case "vibe-scan":
54748
+ return handleScanSecretsCommand(parsed);
54749
+ case "terrain":
54750
+ case "machine-terrain":
54751
+ return handleTerrainCommand(parsed);
53283
54752
  case "code-impact":
53284
54753
  case "impact":
53285
54754
  return handleCodeImpactCommand(parsed);
@@ -53385,9 +54854,12 @@ Ecosystem, Migration & Connectivity:
53385
54854
  daemon [--port N] Real-time agency hub (SSE event stream + publish API, default port 7878)
53386
54855
  sync [status|broadcast|ingest|pool] Cross-agent knowledge sync & P2P gossip mesh
53387
54856
  mesh [query|check|link|unlink|propagate] Multi-repo & monorepo cross-project mesh
53388
- health Run unified 5-pillar architectural health evaluation
54857
+ health [--project P] Run unified 5-pillar architectural health evaluation; --project scopes it to one memory project
54858
+ conflicts [--dry-run] Inspect recorded contradictions; --dry-run previews what would be flagged
53389
54859
  reconcile [--prune] [--stale] Reconcile orphaned & drifted code anchors across memories
53390
54860
  benchmark [--iterations N] Run microsecond speed, quality & token economy benchmark suite
54861
+ scan-secrets [dir] [--json] Native Vibeguard secret scanner (zero external dependencies)
54862
+ terrain [--out file.md] Collect machine hardware, toolchain & agent ecosystem map
53391
54863
  code-impact <file> [--symbol] Synthesize code callers, ADRs, negative warnings & risk score
53392
54864
  pr-context [--base B] [--out] Generate memory-verified PR description from git diff
53393
54865
  mcp Run stdio MCP server for agent platforms
@@ -53412,9 +54884,12 @@ var init_cli = __esm(() => {
53412
54884
  init_lifecycle();
53413
54885
  init_evolution();
53414
54886
  init_health2();
54887
+ init_conflicts();
53415
54888
  init_sync2();
53416
54889
  init_mesh2();
53417
54890
  init_versioning();
54891
+ init_secrets2();
54892
+ init_system();
53418
54893
  });
53419
54894
 
53420
54895
  // bin/memory.ts