@wirecat/cli-memo 0.131.21 → 0.131.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +3 -3
  2. package/dist/bin/memo.js +2 -2
  3. package/dist/bundle/{app-server-26YPLJ7M.js → app-server-PRQVGA5X.js} +5 -5
  4. package/dist/bundle/{auto-QHTPHGNA.js → auto-S3AYMAGB.js} +11 -11
  5. package/dist/bundle/{chunk-JXH22ZGA.js → chunk-3B645KUY.js} +2 -2
  6. package/dist/bundle/{chunk-7NEWBM2O.js → chunk-4SGCOUQ6.js} +2 -2
  7. package/dist/bundle/{chunk-J4GC33JU.js → chunk-5UOOH7VG.js} +1 -1
  8. package/dist/bundle/{chunk-UXCSNSUU.js → chunk-6JOT5KWM.js} +9 -9
  9. package/dist/bundle/{chunk-ZCKU6WMX.js → chunk-7W3RCCRR.js} +1 -1
  10. package/dist/bundle/{chunk-JJ3C5AYK.js → chunk-CBYZW5CC.js} +2 -2
  11. package/dist/bundle/{chunk-TMNHNZVA.js → chunk-HXRIV2TV.js} +2 -2
  12. package/dist/bundle/{chunk-EAYYWS67.js → chunk-IFWBFYXN.js} +1 -1
  13. package/dist/bundle/{chunk-YUFUUQUO.js → chunk-ILC7NFS2.js} +1 -1
  14. package/dist/bundle/{chunk-B3YV6Z7Y.js → chunk-J35OGOCI.js} +1 -1
  15. package/dist/bundle/{chunk-INURBEAO.js → chunk-KXERKAID.js} +1 -1
  16. package/dist/bundle/{chunk-FFWK5NEZ.js → chunk-OFYKXQMS.js} +3 -3
  17. package/dist/bundle/{chunk-NOG5O2NS.js → chunk-SO2YVKBK.js} +4 -4
  18. package/dist/bundle/{chunk-NWB5NAGB.js → chunk-UCINAEIA.js} +2 -2
  19. package/dist/bundle/{chunk-PO5C7VNC.js → chunk-UG2V7BQY.js} +1 -1
  20. package/dist/bundle/{chunk-R26CXR5Z.js → chunk-VDBFSLHP.js} +3 -3
  21. package/dist/bundle/{chunk-CRZSJHAK.js → chunk-XW4KGSAA.js} +1 -1
  22. package/dist/bundle/{chunk-J55Z6EJ3.js → chunk-ZKWE5MCA.js} +605 -9
  23. package/dist/bundle/{entry-DO6ZBFIS.js → entry-L42C5CHF.js} +1 -1
  24. package/dist/bundle/{failure-LHMJNFY6.js → failure-TQJGSWRU.js} +9 -9
  25. package/dist/bundle/{first-run-BXFQN6M5.js → first-run-TT6KMHKA.js} +1 -1
  26. package/dist/bundle/{mcp-A4HPKQK5.js → mcp-PNC3TI7O.js} +3 -3
  27. package/dist/bundle/memo.js +6 -6
  28. package/dist/bundle/{program-3ZTQ5FEM.js → program-ZIRYOO2T.js} +141 -94
  29. package/dist/bundle/{server-NQK5EX7P.js → server-F2PSE6LG.js} +3 -3
  30. package/dist/bundle/{server-UN43Y6BY.js → server-K5JYA3ZT.js} +5 -5
  31. package/dist/bundle/{server-BMQBWBBN.js → server-VO5SXU5A.js} +14 -14
  32. package/dist/bundle/{setup-FGSVWQ32.js → setup-X6OJ3K4F.js} +9 -9
  33. package/dist/bundle/{store-maintenance-command-QTEWWDST.js → store-maintenance-command-R4CP6AZN.js} +3 -3
  34. package/dist/bundle/{update-XC6KAVCK.js → update-M6AIJ2SZ.js} +10 -10
  35. package/dist/examples.d.ts.map +1 -1
  36. package/dist/examples.js +5 -1
  37. package/dist/examples.js.map +1 -1
  38. package/dist/knowledge/command.d.ts.map +1 -1
  39. package/dist/knowledge/command.js +8 -52
  40. package/dist/knowledge/command.js.map +1 -1
  41. package/dist/knowledge/project-gather-command.d.ts.map +1 -1
  42. package/dist/knowledge/project-gather-command.js +32 -19
  43. package/dist/knowledge/project-gather-command.js.map +1 -1
  44. package/dist/knowledge/relationships-scan.d.ts +8 -0
  45. package/dist/knowledge/relationships-scan.d.ts.map +1 -0
  46. package/dist/knowledge/relationships-scan.js +95 -0
  47. package/dist/knowledge/relationships-scan.js.map +1 -0
  48. package/dist/store/source-list.d.ts +2 -0
  49. package/dist/store/source-list.d.ts.map +1 -1
  50. package/dist/store/source-list.js +6 -0
  51. package/dist/store/source-list.js.map +1 -1
  52. package/dist/version.d.ts +1 -1
  53. package/dist/version.js +1 -1
  54. package/package.json +1 -1
  55. package/skills/cli-memo/SKILL.md +1 -1
@@ -21681,7 +21681,7 @@ var peopleService = /* @__PURE__ */ __name((deps) => {
21681
21681
  ...!includeNotes || local.notes.length === 0 ? {} : { notes: local.notes }
21682
21682
  };
21683
21683
  }, "localCard");
21684
- const referenceOf = /* @__PURE__ */ __name(async (reference) => {
21684
+ const referenceOf2 = /* @__PURE__ */ __name(async (reference) => {
21685
21685
  const held = await storeIfOpen(deps);
21686
21686
  if (!held)
21687
21687
  return reference;
@@ -21696,7 +21696,7 @@ var peopleService = /* @__PURE__ */ __name((deps) => {
21696
21696
  }, "online");
21697
21697
  const change = /* @__PURE__ */ __name(async (action2, person, act2) => {
21698
21698
  const connection = await online(`contacts ${action2.slice("contact-".length)}`);
21699
- const [personId] = await capability(connection, "people", "find people")([await referenceOf(person)]);
21699
+ const [personId] = await capability(connection, "people", "find people")([await referenceOf2(person)]);
21700
21700
  const operationId = newOperationId();
21701
21701
  const done2 = await guardedWrite(deps.guard, { operationId, chatId: null, kind: "account", action: action2 }, () => act2(connection, personId));
21702
21702
  return { operationId, personId, done: done2 };
@@ -21720,7 +21720,7 @@ var peopleService = /* @__PURE__ */ __name((deps) => {
21720
21720
  const found = pickPerson(person, await store.people.people(account.provider, { account: account.account }));
21721
21721
  return localCard({ ...found, chats: await sharedChats(store, account, found.id) }, notes);
21722
21722
  }
21723
- const card = await capability(await deps.connection(), "contact", "show a person")(await referenceOf(person));
21723
+ const card = await capability(await deps.connection(), "contact", "show a person")(await referenceOf2(person));
21724
21724
  if (card.chats.length > 0)
21725
21725
  return localCard(card, notes);
21726
21726
  const held = await storeIfOpen(deps);
@@ -23473,6 +23473,393 @@ var excerpt = /* @__PURE__ */ __name((text2, query, size4 = 240, options = {}) =
23473
23473
  }, "excerpt");
23474
23474
  var withExcerpt = /* @__PURE__ */ __name(({ matchedText, ...hit }, query) => ({ ...hit, excerpt: excerpt(matchedText ?? hit.text, query) }), "withExcerpt");
23475
23475
 
23476
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/shared.js
23477
+ var escaped = /* @__PURE__ */ __name((text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "escaped");
23478
+ var folded = /* @__PURE__ */ __name((text2) => text2.normalize("NFKC").toLowerCase().replace(/\s+/gu, " "), "folded");
23479
+ var wholeWords = /* @__PURE__ */ __name((term) => new RegExp(`(?<![\\p{L}\\p{N}\\p{M}_])${escaped(normalizeVocabularyName(term))}(?![\\p{L}\\p{N}\\p{M}_])`, "u"), "wholeWords");
23480
+ var projectTerms = /* @__PURE__ */ __name((project2) => {
23481
+ const terms = /* @__PURE__ */ new Map();
23482
+ for (const term of [project2.name, project2.key, ...project2.aliases])
23483
+ if (term?.trim())
23484
+ terms.set(normalizeVocabularyName(term), term);
23485
+ return [...terms.values()];
23486
+ }, "projectTerms");
23487
+ var namesProject = /* @__PURE__ */ __name((project2) => {
23488
+ const patterns = projectTerms(project2).map((term) => ({ term, pattern: wholeWords(term) }));
23489
+ return (text2) => {
23490
+ if (!text2)
23491
+ return void 0;
23492
+ const value = folded(text2);
23493
+ return patterns.find(({ pattern }) => pattern.test(value))?.term;
23494
+ };
23495
+ }, "namesProject");
23496
+ var chatRef2 = /* @__PURE__ */ __name((key, chat) => formatReference({ type: "chat", provider: key.provider, account: String(key.account), chat: String(chat) }), "chatRef");
23497
+ var contactRef2 = /* @__PURE__ */ __name((provider, id2) => formatReference({ type: "contact", provider, id: String(id2) }), "contactRef");
23498
+ var sameAccount = /* @__PURE__ */ __name((a, b) => a.provider === b.provider && String(a.account) === b.account, "sameAccount");
23499
+ var personRefOf = /* @__PURE__ */ __name(async (store, provider, id2) => {
23500
+ const person = await store.people.personOf({ provider, id: id2 });
23501
+ return person ? `person:${person.uid}` : void 0;
23502
+ }, "personRefOf");
23503
+ var projectsOf = /* @__PURE__ */ __name(async (store, person) => new Set((await store.knowledge.relations(null, person)).filter((one2) => one2.confirmed && one2.kind === "member-of" && one2.from === person && one2.to.startsWith("project:")).map((one2) => one2.to)).size, "projectsOf");
23504
+ var identitiesIn = /* @__PURE__ */ __name(async (store, people, provider) => {
23505
+ const found = [];
23506
+ for (const person of people) {
23507
+ const record2 = await store.people.personByUid(person.slice("person:".length));
23508
+ for (const identity of record2?.identities ?? [])
23509
+ if (identity.provider === provider)
23510
+ found.push({ person, id: String(identity.id) });
23511
+ }
23512
+ return found;
23513
+ }, "identitiesIn");
23514
+
23515
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/alias-hits.js
23516
+ var SEARCH_LIMIT = 500;
23517
+ var aliasWeight = /* @__PURE__ */ __name((hits) => hits >= 10 ? 0.55 : hits >= 3 ? 0.35 : 0, "aliasWeight");
23518
+ var aliasHits = {
23519
+ id: "alias-hits",
23520
+ version: 1,
23521
+ candidates: /* @__PURE__ */ __name(async ({ store, project: project2, sources, since, signal }) => {
23522
+ if (!project2)
23523
+ return [];
23524
+ const terms = projectTerms(project2).filter((term) => [...term.trim()].length >= 3);
23525
+ const found = [];
23526
+ for (const key of sources) {
23527
+ const chats = /* @__PURE__ */ new Map();
23528
+ for (const term of terms) {
23529
+ signal?.throwIfAborted();
23530
+ const pattern = wholeWords(term);
23531
+ const hits = await store.messages.find({ account: key, text: term, limit: SEARCH_LIMIT });
23532
+ for (const hit of hits.items) {
23533
+ if (hit.timestamp < since || !pattern.test(hit.text.normalize("NFKC").toLowerCase()))
23534
+ continue;
23535
+ const messages = chats.get(String(hit.chatId)) ?? /* @__PURE__ */ new Map();
23536
+ messages.set(String(hit.id), formatReference({
23537
+ type: "message",
23538
+ provider: key.provider,
23539
+ account: String(key.account),
23540
+ chat: String(hit.chatId),
23541
+ message: String(hit.id)
23542
+ }));
23543
+ chats.set(String(hit.chatId), messages);
23544
+ }
23545
+ }
23546
+ for (const [chat, messages] of chats) {
23547
+ const weight = aliasWeight(messages.size);
23548
+ if (!weight)
23549
+ continue;
23550
+ found.push({
23551
+ from: chatRef2(key, chat),
23552
+ to: project2.ref,
23553
+ kind: "member-of",
23554
+ weight,
23555
+ why: `${messages.size} messages since ${since.slice(0, 10)} name the project`,
23556
+ evidence: [...messages.values()].slice(0, 3)
23557
+ });
23558
+ }
23559
+ }
23560
+ return found;
23561
+ }, "candidates")
23562
+ };
23563
+
23564
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/chat-senders.js
23565
+ var WEIGHT = 0.5;
23566
+ var LARGE_GROUP_WEIGHT = 0.2;
23567
+ var LARGE_GROUP = 30;
23568
+ var MIN_MESSAGES = 5;
23569
+ var READ_LIMIT = 2e3;
23570
+ var chatSenders = {
23571
+ id: "chat-senders",
23572
+ version: 1,
23573
+ candidates: /* @__PURE__ */ __name(async ({ store, project: project2, confirmed, sources, since, owner, signal }) => {
23574
+ if (!project2)
23575
+ return [];
23576
+ const found = [];
23577
+ for (const chat of confirmed.chats) {
23578
+ const reference = parseReference(chat);
23579
+ if (reference.type !== "chat")
23580
+ continue;
23581
+ const key = sources.find((source) => sameAccount(source, reference));
23582
+ if (!key)
23583
+ continue;
23584
+ signal?.throwIfAborted();
23585
+ const page = await store.messages.messages(key, reference.chat, { limit: READ_LIMIT, since, outgoing: false });
23586
+ const senders = /* @__PURE__ */ new Map();
23587
+ for (const message of page.items) {
23588
+ if (message.senderId === null || message.senderIsChat)
23589
+ continue;
23590
+ const id2 = String(message.senderId);
23591
+ senders.set(id2, { count: (senders.get(id2)?.count ?? 0) + 1, last: String(message.id) });
23592
+ }
23593
+ const size4 = Math.max((await store.roster.members(key, reference.chat)).length, senders.size);
23594
+ const weight = size4 > LARGE_GROUP ? LARGE_GROUP_WEIGHT : WEIGHT;
23595
+ for (const [id2, { count: count4, last }] of senders) {
23596
+ if (count4 < MIN_MESSAGES)
23597
+ continue;
23598
+ const person = await personRefOf(store, key.provider, id2);
23599
+ if (!person || person === owner)
23600
+ continue;
23601
+ found.push({
23602
+ from: person,
23603
+ to: project2.ref,
23604
+ kind: "member-of",
23605
+ weight,
23606
+ why: `${count4} messages since ${since.slice(0, 10)} in a project chat (${chat})${size4 > LARGE_GROUP ? `, a group of ${size4}` : ""}`,
23607
+ evidence: [formatReference({ ...reference, type: "message", message: last })],
23608
+ identity: contactRef2(key.provider, id2)
23609
+ });
23610
+ }
23611
+ }
23612
+ return found;
23613
+ }, "candidates")
23614
+ };
23615
+
23616
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/chat-title.js
23617
+ var WEIGHT2 = 0.7;
23618
+ var chatTitle = {
23619
+ id: "chat-title",
23620
+ version: 1,
23621
+ candidates: /* @__PURE__ */ __name(async ({ store, project: project2, sources, purposes, signal }) => {
23622
+ if (!project2)
23623
+ return [];
23624
+ const names2 = namesProject(project2);
23625
+ const purposeOf = purposes.filter((purpose) => purpose !== "work").map((purpose) => ({ purpose, pattern: wholeWords(purpose) }));
23626
+ const found = [];
23627
+ for (const key of sources) {
23628
+ if (key.provider === "email")
23629
+ continue;
23630
+ signal?.throwIfAborted();
23631
+ const chats = await store.chats.chats(key, {});
23632
+ for (const chat of chats.items) {
23633
+ if (chat.kind === "saved")
23634
+ continue;
23635
+ const term = names2(chat.title);
23636
+ if (!term)
23637
+ continue;
23638
+ const title = (chat.title ?? "").normalize("NFKC").toLowerCase();
23639
+ const purpose = purposeOf.find(({ pattern }) => pattern.test(title))?.purpose;
23640
+ found.push({
23641
+ from: chatRef2(key, chat.id),
23642
+ to: project2.ref,
23643
+ kind: "member-of",
23644
+ weight: WEIGHT2,
23645
+ why: `the chat title names the project${term === project2.name ? "" : ` (${term === project2.key ? "key" : "alias"})`}`,
23646
+ evidence: [],
23647
+ ...purpose ? { purpose } : {}
23648
+ });
23649
+ }
23650
+ }
23651
+ return found;
23652
+ }, "candidates")
23653
+ };
23654
+
23655
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/email-domain.js
23656
+ var WEIGHT3 = 0.3;
23657
+ var CONTACTS = 500;
23658
+ var MOST_PAIRS = 200;
23659
+ var FREE = /* @__PURE__ */ new Set([
23660
+ "gmail.com",
23661
+ "outlook.com",
23662
+ "hotmail.com",
23663
+ "yahoo.com",
23664
+ "icloud.com",
23665
+ "proton.me",
23666
+ "protonmail.com"
23667
+ ]);
23668
+ var emailDomain = {
23669
+ id: "email-domain",
23670
+ version: 1,
23671
+ candidates: /* @__PURE__ */ __name(async ({ store, sources, owner, signal }) => {
23672
+ const domains = /* @__PURE__ */ new Map();
23673
+ for (const key of sources) {
23674
+ if (key.provider !== "email")
23675
+ continue;
23676
+ signal?.throwIfAborted();
23677
+ const contacts = await store.people.contacts(key, { order: "name", limit: CONTACTS, offset: 0 });
23678
+ for (const contact of contacts.items) {
23679
+ const address = String(contact.id).toLowerCase();
23680
+ const domain = address.split("@")[1];
23681
+ if (!domain || FREE.has(domain))
23682
+ continue;
23683
+ const person = await store.people.personOf({ provider: key.provider, id: contact.id });
23684
+ if (!person || `person:${person.uid}` === owner)
23685
+ continue;
23686
+ const people = domains.get(domain) ?? /* @__PURE__ */ new Map();
23687
+ if (!people.has(`person:${person.uid}`))
23688
+ people.set(`person:${person.uid}`, contactRef2(key.provider, contact.id));
23689
+ domains.set(domain, people);
23690
+ }
23691
+ }
23692
+ const found = [];
23693
+ for (const [domain, people] of domains) {
23694
+ const sorted = [...people].sort(([a], [b]) => a.localeCompare(b));
23695
+ for (let left = 0; left < sorted.length; left++)
23696
+ for (let right = left + 1; right < sorted.length; right++) {
23697
+ if (found.length >= MOST_PAIRS)
23698
+ return found;
23699
+ const [from, identity] = sorted[left];
23700
+ const [to] = sorted[right];
23701
+ found.push({
23702
+ from,
23703
+ to,
23704
+ kind: "related-to",
23705
+ weight: WEIGHT3,
23706
+ why: `addresses share the domain ${domain}; this does not prove they work together`,
23707
+ evidence: [],
23708
+ identity
23709
+ });
23710
+ }
23711
+ }
23712
+ return found;
23713
+ }, "candidates")
23714
+ };
23715
+
23716
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/mail-thread.js
23717
+ var NAMED = 0.7;
23718
+ var MEMBER_ONLY = 0.3;
23719
+ var READ_LIMIT2 = 500;
23720
+ var mailThread = {
23721
+ id: "mail-thread",
23722
+ version: 1,
23723
+ candidates: /* @__PURE__ */ __name(async ({ store, project: project2, confirmed, sources, since, owner, signal }) => {
23724
+ if (!project2)
23725
+ return [];
23726
+ const names2 = namesProject(project2);
23727
+ const people = confirmed.people.filter((person) => person !== owner);
23728
+ const found = [];
23729
+ for (const key of sources) {
23730
+ if (key.provider !== "email")
23731
+ continue;
23732
+ const account = await store.accounts.storedAccount(key).catch(() => void 0);
23733
+ if (!account)
23734
+ continue;
23735
+ const threads = /* @__PURE__ */ new Map();
23736
+ for (const { person, id: id2 } of await identitiesIn(store, people, "email")) {
23737
+ signal?.throwIfAborted();
23738
+ const emails = await store.mail.emails({
23739
+ accountId: account.id,
23740
+ participant: id2,
23741
+ since: Date.parse(since),
23742
+ limit: READ_LIMIT2
23743
+ });
23744
+ for (const email of emails) {
23745
+ const held = threads.get(email.emailThreadId) ?? {
23746
+ members: /* @__PURE__ */ new Set(),
23747
+ subject: email.subject,
23748
+ email: email.externalId
23749
+ };
23750
+ held.members.add(person);
23751
+ threads.set(email.emailThreadId, held);
23752
+ }
23753
+ }
23754
+ for (const [id2, { members, subject, email }] of threads) {
23755
+ const thread = (await store.mail.thread(id2))?.thread;
23756
+ if (!thread)
23757
+ continue;
23758
+ const named2 = names2(thread.subject ?? subject) !== void 0;
23759
+ const chat = formatReference({
23760
+ type: "chat",
23761
+ provider: "email",
23762
+ account: String(key.account),
23763
+ chat: thread.externalId
23764
+ });
23765
+ const who2 = [...members].sort();
23766
+ found.push({
23767
+ from: chat,
23768
+ to: project2.ref,
23769
+ kind: "member-of",
23770
+ weight: named2 ? NAMED : MEMBER_ONLY,
23771
+ why: `a thread with ${who2.length > 1 ? `${who2.length} members` : `a member (${who2[0]})`}${named2 ? "; its subject names the project" : ""}`,
23772
+ evidence: [
23773
+ formatReference({
23774
+ type: "message",
23775
+ provider: "email",
23776
+ account: String(key.account),
23777
+ chat: thread.externalId,
23778
+ message: email
23779
+ }),
23780
+ ...who2
23781
+ ].slice(0, 5)
23782
+ });
23783
+ }
23784
+ }
23785
+ return found;
23786
+ }, "candidates")
23787
+ };
23788
+
23789
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/member-chats.js
23790
+ var WEIGHT4 = 0.5;
23791
+ var memberChats = {
23792
+ id: "member-chats",
23793
+ version: 1,
23794
+ candidates: /* @__PURE__ */ __name(async ({ store, project: project2, confirmed, sources, owner, signal }) => {
23795
+ if (!project2)
23796
+ return [];
23797
+ const people = confirmed.people.filter((person) => person !== owner);
23798
+ const found = [];
23799
+ for (const key of sources) {
23800
+ const members = /* @__PURE__ */ new Map();
23801
+ for (const { person, id: id2 } of await identitiesIn(store, people, key.provider)) {
23802
+ signal?.throwIfAborted();
23803
+ for (const chat of await store.chats.chatsWith(key, id2)) {
23804
+ if (chat.kind !== "group" && chat.kind !== "channel")
23805
+ continue;
23806
+ const held = members.get(String(chat.id)) ?? /* @__PURE__ */ new Set();
23807
+ held.add(person);
23808
+ members.set(String(chat.id), held);
23809
+ }
23810
+ }
23811
+ for (const [chat, held] of members) {
23812
+ if (held.size < 2)
23813
+ continue;
23814
+ const named2 = [...held].sort();
23815
+ found.push({
23816
+ from: chatRef2(key, chat),
23817
+ to: project2.ref,
23818
+ kind: "member-of",
23819
+ weight: WEIGHT4,
23820
+ why: `a group with ${held.size} confirmed members (${named2.slice(0, 3).join(", ")}${held.size > 3 ? ", \u2026" : ""})`,
23821
+ evidence: named2.slice(0, 5)
23822
+ });
23823
+ }
23824
+ }
23825
+ return found;
23826
+ }, "candidates")
23827
+ };
23828
+
23829
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/member-dialog.js
23830
+ var WEIGHT5 = 0.6;
23831
+ var memberDialog = {
23832
+ id: "member-dialog",
23833
+ version: 1,
23834
+ candidates: /* @__PURE__ */ __name(async ({ store, project: project2, confirmed, sources, owner, signal }) => {
23835
+ if (!project2)
23836
+ return [];
23837
+ const people = confirmed.people.filter((person) => person !== owner);
23838
+ const projects = /* @__PURE__ */ new Map();
23839
+ const found = [];
23840
+ for (const key of sources)
23841
+ for (const { person, id: id2 } of await identitiesIn(store, people, key.provider)) {
23842
+ signal?.throwIfAborted();
23843
+ for (const chat of await store.chats.chatsWith(key, id2)) {
23844
+ if (chat.kind !== "dialog")
23845
+ continue;
23846
+ if (!projects.has(person))
23847
+ projects.set(person, Math.max(1, await projectsOf(store, person)));
23848
+ const shared = projects.get(person) ?? 1;
23849
+ found.push({
23850
+ from: chatRef2(key, chat.id),
23851
+ to: project2.ref,
23852
+ kind: "member-of",
23853
+ weight: WEIGHT5 / shared,
23854
+ why: `a dialog with a member (${person})${shared > 1 ? `, who is in ${shared} projects` : ""}`,
23855
+ evidence: [person]
23856
+ });
23857
+ }
23858
+ }
23859
+ return found;
23860
+ }, "candidates")
23861
+ };
23862
+
23476
23863
  // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/project-gathering-around.js
23477
23864
  var storedMessageNeighbours = /* @__PURE__ */ __name((messages) => ({
23478
23865
  async around(ref, window3) {
@@ -24694,6 +25081,214 @@ var purposeHintOf = /* @__PURE__ */ __name((linked, rows, of) => {
24694
25081
  return { chat: top.ref, relationship: link4.relationship, items, of };
24695
25082
  }, "purposeHintOf");
24696
25083
 
25084
+ // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/link-scorers/scan.js
25085
+ var SCORERS = [chatTitle, aliasHits, memberChats, memberDialog, chatSenders, mailThread];
25086
+ var PERSON_SCORERS = [emailDomain];
25087
+ var SCAN_BOT = { bot: "memo-scan", kind: "rule" };
25088
+ var DEFAULT_THRESHOLD = 0.5;
25089
+ var PERSON_THRESHOLD = 0.3;
25090
+ var DEFAULT_LIMIT = 20;
25091
+ var projectOf3 = /* @__PURE__ */ __name(async (store, reference) => {
25092
+ const ref = await canonicalProjectRef(store, reference);
25093
+ const entry = (await store.knowledge.vocabulary({ kind: "project" })).entries.find((one2) => one2.id === ref.slice(8));
25094
+ if (!entry)
25095
+ throw new CliError("not_found", `no project ${reference}`);
25096
+ return { ref, name: entry.name, key: entry.key, aliases: entry.aliases.map((alias) => alias.name) };
25097
+ }, "projectOf");
25098
+ var confirmedOf = /* @__PURE__ */ __name(async (store, project2) => {
25099
+ if (!project2)
25100
+ return { chats: [], people: [] };
25101
+ const members = (await store.knowledge.relations(null, project2.ref)).filter((one2) => one2.confirmed && one2.kind === "member-of" && one2.to === project2.ref);
25102
+ return {
25103
+ chats: [...new Set(members.map((one2) => one2.from).filter((ref) => ref.startsWith("chat:")))].sort(),
25104
+ people: [...new Set(members.map((one2) => one2.from).filter((ref) => ref.startsWith("person:")))].sort()
25105
+ };
25106
+ }, "confirmedOf");
25107
+ var checkedSources = /* @__PURE__ */ __name(async (store, sources) => {
25108
+ const stored = await store.accounts.accounts();
25109
+ const choices = stored.map((one2) => `${one2.provider}:${one2.account}`);
25110
+ if (!Array.isArray(sources) || sources.length === 0)
25111
+ throw new CliError("validation_error", "a scan names the accounts it reads; none is picked for you", {
25112
+ param: "source",
25113
+ choices
25114
+ });
25115
+ for (const source of sources)
25116
+ if (!stored.some((one2) => one2.provider === source.provider && String(one2.account) === String(source.account)))
25117
+ throw new CliError("not_found", `the store holds no account ${source.provider}:${source.account}`, {
25118
+ param: "source",
25119
+ choices
25120
+ });
25121
+ return sources;
25122
+ }, "checkedSources");
25123
+ var pairKey = /* @__PURE__ */ __name(({ from, to, kind }) => `${from}\0${to}\0${kind}`, "pairKey");
25124
+ var combinedScore = /* @__PURE__ */ __name((weights) => Math.round((1 - weights.reduce((left, weight) => left * (1 - weight), 1)) * 1e3) / 1e3, "combinedScore");
25125
+ var scanProjectLinks = /* @__PURE__ */ __name(async (store, input) => {
25126
+ const limit = input.limit ?? DEFAULT_LIMIT;
25127
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 50)
25128
+ throw new CliError("validation_error", "a scan proposes 1\u201350 links a run", { param: "limit" });
25129
+ const threshold = input.threshold ?? (input.project === null ? PERSON_THRESHOLD : DEFAULT_THRESHOLD);
25130
+ if (typeof threshold !== "number" || !(threshold > 0 && threshold <= 1))
25131
+ throw new CliError("validation_error", "a scan's threshold is more than 0 and at most 1", { param: "threshold" });
25132
+ const since = Date.parse(input.since);
25133
+ if (Number.isNaN(since))
25134
+ throw new CliError("validation_error", "a scan's window starts at an ISO 8601 time", { param: "since" });
25135
+ const sources = await checkedSources(store, input.sources);
25136
+ const project2 = input.project === null ? null : await projectOf3(store, input.project);
25137
+ const owner = await store.people.owner();
25138
+ const scorerInput = {
25139
+ store,
25140
+ project: project2,
25141
+ confirmed: await confirmedOf(store, project2),
25142
+ sources,
25143
+ since: new Date(since).toISOString(),
25144
+ purposes: input.purposes ?? BUILT_IN_PURPOSES,
25145
+ owner: owner ? `person:${owner.uid}` : null,
25146
+ ...input.signal ? { signal: input.signal } : {}
25147
+ };
25148
+ const scorers = input.scorers ?? (project2 ? SCORERS : PERSON_SCORERS);
25149
+ const skipped = {
25150
+ confirmed: 0,
25151
+ rejected: 0,
25152
+ belowThreshold: 0,
25153
+ overLimit: 0,
25154
+ nameOnly: 0,
25155
+ unresolved: 0
25156
+ };
25157
+ const pairs = /* @__PURE__ */ new Map();
25158
+ const nameOnly = /* @__PURE__ */ new Set();
25159
+ for (const scorer of scorers) {
25160
+ input.signal?.throwIfAborted();
25161
+ for (const candidate of await scorer.candidates(scorerInput)) {
25162
+ const key = pairKey(candidate);
25163
+ const verdict = await admissible(store, scorerInput, candidate);
25164
+ if (verdict === "owner")
25165
+ continue;
25166
+ if (verdict === "name-only") {
25167
+ if (!nameOnly.has(key))
25168
+ skipped.nameOnly++;
25169
+ nameOnly.add(key);
25170
+ continue;
25171
+ }
25172
+ if (verdict !== "ok") {
25173
+ skipped.unresolved++;
25174
+ continue;
25175
+ }
25176
+ const weight = Math.min(1, Math.max(0, candidate.weight));
25177
+ const held = pairs.get(key) ?? { candidate, signals: /* @__PURE__ */ new Map() };
25178
+ const scorerId = `${scorer.id}@${scorer.version}`;
25179
+ const before2 = held.signals.get(scorerId);
25180
+ if (!before2 || before2.weight < weight)
25181
+ held.signals.set(scorerId, {
25182
+ scorer: scorerId,
25183
+ weight,
25184
+ why: candidate.why.slice(0, 500),
25185
+ ...candidate.purpose && scorerInput.purposes.includes(candidate.purpose) ? { purpose: candidate.purpose } : {}
25186
+ });
25187
+ held.candidate = {
25188
+ ...held.candidate,
25189
+ evidence: [.../* @__PURE__ */ new Set([...held.candidate.evidence, ...candidate.evidence])]
25190
+ };
25191
+ pairs.set(key, held);
25192
+ }
25193
+ }
25194
+ const scored = [...pairs.values()].map(({ candidate, signals: signals2 }) => {
25195
+ const list3 = [...signals2.values()].sort((a, b) => b.weight - a.weight || a.scorer.localeCompare(b.scorer));
25196
+ const purpose = list3.find((one2) => one2.purpose)?.purpose;
25197
+ return {
25198
+ candidate,
25199
+ purpose,
25200
+ signals: list3.map(({ scorer, weight, why }) => ({ scorer, weight, why })),
25201
+ score: combinedScore(list3.map((one2) => one2.weight))
25202
+ };
25203
+ }).sort((a, b) => b.score - a.score || a.candidate.from.localeCompare(b.candidate.from));
25204
+ const proposals = [];
25205
+ let fresh = 0;
25206
+ for (const { candidate, purpose, signals: signals2, score } of scored) {
25207
+ const state = await store.knowledge.relationState(null, candidate);
25208
+ if (!state) {
25209
+ skipped.unresolved++;
25210
+ continue;
25211
+ }
25212
+ if (state.linked) {
25213
+ skipped.confirmed++;
25214
+ continue;
25215
+ }
25216
+ if (state.rejected || await rejectedReversed(store, candidate)) {
25217
+ skipped.rejected++;
25218
+ continue;
25219
+ }
25220
+ if (score < threshold) {
25221
+ skipped.belowThreshold++;
25222
+ continue;
25223
+ }
25224
+ if (!state.pending && fresh >= limit) {
25225
+ skipped.overLimit++;
25226
+ continue;
25227
+ }
25228
+ if (!state.pending)
25229
+ fresh++;
25230
+ const { identity: _identity, purpose: _purpose, weight: _weight, why: _why, ...pair } = candidate;
25231
+ const evidence = candidate.evidence.slice(0, 5);
25232
+ const proposal = {
25233
+ ...pair,
25234
+ weight: score,
25235
+ why: signals2.map((one2) => one2.why).join("; "),
25236
+ evidence,
25237
+ ...purpose ? { purpose } : {},
25238
+ score,
25239
+ signals: signals2
25240
+ };
25241
+ if (!input.write) {
25242
+ proposals.push({ ...proposal, id: state.pending?.id ?? null, state: "dry-run" });
25243
+ continue;
25244
+ }
25245
+ const written = await store.knowledge.proposeRelation(null, {
25246
+ from: pair.from,
25247
+ to: pair.to,
25248
+ kind: pair.kind,
25249
+ evidence,
25250
+ reason: proposal.why.slice(0, 2e3),
25251
+ score,
25252
+ signals: signals2,
25253
+ ...purpose ? { purpose } : {}
25254
+ }, SCAN_BOT);
25255
+ proposals.push({
25256
+ ...proposal,
25257
+ id: written.id,
25258
+ state: !state.pending ? "new" : JSON.stringify(written.payload) === JSON.stringify(state.pending.payload) ? "unchanged" : "updated"
25259
+ });
25260
+ }
25261
+ return { project: project2?.ref ?? null, proposals, skipped };
25262
+ }, "scanProjectLinks");
25263
+ var admissible = /* @__PURE__ */ __name(async (store, input, candidate) => {
25264
+ if (candidate.from === candidate.to || !Number.isFinite(candidate.weight))
25265
+ return "unresolved";
25266
+ if (input.project && candidate.to !== input.project.ref && !candidate.to.startsWith("person:"))
25267
+ return "unresolved";
25268
+ if (candidate.from === input.owner || candidate.to === input.owner)
25269
+ return "owner";
25270
+ if (candidate.from.startsWith("person:")) {
25271
+ const identity = candidate.identity === void 0 ? void 0 : referenceOf(candidate.identity);
25272
+ if (identity?.type !== "contact")
25273
+ return "name-only";
25274
+ const person = await store.people.personOf({ provider: identity.provider, id: identity.id });
25275
+ return person !== void 0 && `person:${person.uid}` === candidate.from ? "ok" : "name-only";
25276
+ }
25277
+ if (candidate.from.startsWith("chat:")) {
25278
+ const chat = referenceOf(candidate.from);
25279
+ return chat?.type === "chat" && input.sources.some((source) => sameAccount(source, chat)) ? "ok" : "unresolved";
25280
+ }
25281
+ return "unresolved";
25282
+ }, "admissible");
25283
+ var referenceOf = /* @__PURE__ */ __name((text2) => {
25284
+ try {
25285
+ return parseReference(text2);
25286
+ } catch {
25287
+ return void 0;
25288
+ }
25289
+ }, "referenceOf");
25290
+ var rejectedReversed = /* @__PURE__ */ __name(async (store, candidate) => candidate.kind === "related-to" && candidate.to.startsWith("person:") && ((await store.knowledge.relationState(null, { from: candidate.to, to: candidate.from, kind: candidate.kind }))?.rejected ?? false), "rejectedReversed");
25291
+
24697
25292
  // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/media-archive.js
24698
25293
  import { setTimeout as sleep3 } from "node:timers/promises";
24699
25294
 
@@ -26283,13 +26878,13 @@ var replyNext = {
26283
26878
  };
26284
26879
 
26285
26880
  // node_modules/.pnpm/@wirecat+cli-core@0.560.0_@wirecat+cli-base@0.75.0_commander@15.0.0_esbuild@0.28.2_type_5702f943a9224d209a94753f662cf36c/node_modules/@wirecat/cli-core/dist/services/project-status/signals/topics.js
26286
- var escaped = /* @__PURE__ */ __name((text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "escaped");
26287
- var wholeWord = /* @__PURE__ */ __name((word) => new RegExp(`(?<![\\p{L}\\p{N}])${escaped(word)}(?![\\p{L}\\p{N}])`, "u"), "wholeWord");
26881
+ var escaped2 = /* @__PURE__ */ __name((text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "escaped");
26882
+ var wholeWord = /* @__PURE__ */ __name((word) => new RegExp(`(?<![\\p{L}\\p{N}])${escaped2(word)}(?![\\p{L}\\p{N}])`, "u"), "wholeWord");
26288
26883
  var matcherOf = /* @__PURE__ */ __name((query) => {
26289
26884
  const words3 = normalize(query).split(" ").filter(Boolean).map(wholeWord);
26290
26885
  return (text2) => {
26291
- const folded = normalize(text2);
26292
- return words3.length > 0 && words3.every((word) => word.test(folded));
26886
+ const folded2 = normalize(text2);
26887
+ return words3.length > 0 && words3.every((word) => word.test(folded2));
26293
26888
  };
26294
26889
  }, "matcherOf");
26295
26890
  var topics = {
@@ -28064,8 +28659,8 @@ var refusedPlace = /* @__PURE__ */ __name((path, app, env, platform = process.pl
28064
28659
  ancestor = parent;
28065
28660
  }
28066
28661
  }
28067
- const folded = platform === "darwin" || platform === "win32";
28068
- const fold2 = /* @__PURE__ */ __name((text2) => folded ? text2.toLowerCase() : text2, "fold");
28662
+ const folded2 = platform === "darwin" || platform === "win32";
28663
+ const fold2 = /* @__PURE__ */ __name((text2) => folded2 ? text2.toLowerCase() : text2, "fold");
28069
28664
  const parts = new Set([...CREDENTIAL_PARTS].map(fold2));
28070
28665
  const runs = CREDENTIAL_RUNS.map((run) => run.map(fold2));
28071
28666
  const named2 = /* @__PURE__ */ __name((candidate) => {
@@ -38499,6 +39094,7 @@ export {
38499
39094
  documentPassage,
38500
39095
  storedNeighbours,
38501
39096
  gatherStoredProject,
39097
+ scanProjectLinks,
38502
39098
  validateMediaSince,
38503
39099
  checkMediaArchive,
38504
39100
  archiveMedia,
@@ -3,7 +3,7 @@ const require = __bundleRequire(import.meta.url);
3
3
  import {
4
4
  openBrowser,
5
5
  startCli
6
- } from "./chunk-ZCKU6WMX.js";
6
+ } from "./chunk-7W3RCCRR.js";
7
7
  import "./chunk-NELE7AIX.js";
8
8
  import "./chunk-OUF6RCRZ.js";
9
9
  import "./chunk-2NJUPIAO.js";