@leemour/cli-memo 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -2
  3. package/dist/app.d.ts +9 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +10 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/bin/memo.d.ts +3 -0
  8. package/dist/bin/memo.d.ts.map +1 -0
  9. package/dist/bin/memo.js +14 -0
  10. package/dist/bin/memo.js.map +1 -0
  11. package/dist/config.d.ts +25 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +20 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/context/command.d.ts +6 -0
  16. package/dist/context/command.d.ts.map +1 -0
  17. package/dist/context/command.js +56 -0
  18. package/dist/context/command.js.map +1 -0
  19. package/dist/context/context.d.ts +34 -0
  20. package/dist/context/context.d.ts.map +1 -0
  21. package/dist/context/context.js +50 -0
  22. package/dist/context/context.js.map +1 -0
  23. package/dist/mail/command.d.ts +4 -0
  24. package/dist/mail/command.d.ts.map +1 -0
  25. package/dist/mail/command.js +56 -0
  26. package/dist/mail/command.js.map +1 -0
  27. package/dist/mail/gmail.d.ts +14 -0
  28. package/dist/mail/gmail.d.ts.map +1 -0
  29. package/dist/mail/gmail.js +57 -0
  30. package/dist/mail/gmail.js.map +1 -0
  31. package/dist/mail/himalaya.d.ts +4 -0
  32. package/dist/mail/himalaya.d.ts.map +1 -0
  33. package/dist/mail/himalaya.js +13 -0
  34. package/dist/mail/himalaya.js.map +1 -0
  35. package/dist/mail/import.d.ts +29 -0
  36. package/dist/mail/import.d.ts.map +1 -0
  37. package/dist/mail/import.js +78 -0
  38. package/dist/mail/import.js.map +1 -0
  39. package/dist/mail/parse.d.ts +13 -0
  40. package/dist/mail/parse.d.ts.map +1 -0
  41. package/dist/mail/parse.js +53 -0
  42. package/dist/mail/parse.js.map +1 -0
  43. package/dist/notes/command.d.ts +4 -0
  44. package/dist/notes/command.d.ts.map +1 -0
  45. package/dist/notes/command.js +103 -0
  46. package/dist/notes/command.js.map +1 -0
  47. package/dist/notes/embed.d.ts +33 -0
  48. package/dist/notes/embed.d.ts.map +1 -0
  49. package/dist/notes/embed.js +74 -0
  50. package/dist/notes/embed.js.map +1 -0
  51. package/dist/notes/files.d.ts +8 -0
  52. package/dist/notes/files.d.ts.map +1 -0
  53. package/dist/notes/files.js +42 -0
  54. package/dist/notes/files.js.map +1 -0
  55. package/dist/notes/import.d.ts +24 -0
  56. package/dist/notes/import.d.ts.map +1 -0
  57. package/dist/notes/import.js +129 -0
  58. package/dist/notes/import.js.map +1 -0
  59. package/dist/notes/read.d.ts +31 -0
  60. package/dist/notes/read.d.ts.map +1 -0
  61. package/dist/notes/read.js +97 -0
  62. package/dist/notes/read.js.map +1 -0
  63. package/dist/notes/search.d.ts +43 -0
  64. package/dist/notes/search.d.ts.map +1 -0
  65. package/dist/notes/search.js +83 -0
  66. package/dist/notes/search.js.map +1 -0
  67. package/dist/notes/text.d.ts +3 -0
  68. package/dist/notes/text.d.ts.map +1 -0
  69. package/dist/notes/text.js +13 -0
  70. package/dist/notes/text.js.map +1 -0
  71. package/dist/options.d.ts +2 -0
  72. package/dist/options.d.ts.map +1 -0
  73. package/dist/options.js +8 -0
  74. package/dist/options.js.map +1 -0
  75. package/dist/people/command.d.ts +5 -0
  76. package/dist/people/command.d.ts.map +1 -0
  77. package/dist/people/command.js +34 -0
  78. package/dist/people/command.js.map +1 -0
  79. package/dist/people/identity.d.ts +4 -0
  80. package/dist/people/identity.d.ts.map +1 -0
  81. package/dist/people/identity.js +13 -0
  82. package/dist/people/identity.js.map +1 -0
  83. package/dist/people/notes-map.d.ts +8 -0
  84. package/dist/people/notes-map.d.ts.map +1 -0
  85. package/dist/people/notes-map.js +8 -0
  86. package/dist/people/notes-map.js.map +1 -0
  87. package/dist/program.d.ts +7 -0
  88. package/dist/program.d.ts.map +1 -0
  89. package/dist/program.js +22 -0
  90. package/dist/program.js.map +1 -0
  91. package/dist/run/auto.d.ts +20 -0
  92. package/dist/run/auto.d.ts.map +1 -0
  93. package/dist/run/auto.js +139 -0
  94. package/dist/run/auto.js.map +1 -0
  95. package/dist/run/import.d.ts +18 -0
  96. package/dist/run/import.d.ts.map +1 -0
  97. package/dist/run/import.js +92 -0
  98. package/dist/run/import.js.map +1 -0
  99. package/dist/store/gone.d.ts +14 -0
  100. package/dist/store/gone.d.ts.map +1 -0
  101. package/dist/store/gone.js +48 -0
  102. package/dist/store/gone.js.map +1 -0
  103. package/dist/version.d.ts +2 -0
  104. package/dist/version.d.ts.map +1 -0
  105. package/dist/version.js +2 -0
  106. package/dist/version.js.map +1 -0
  107. package/package.json +53 -4
@@ -0,0 +1,129 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, statSync } from "node:fs";
3
+ import { basename, dirname, relative, resolve, sep } from "node:path";
4
+ import { dropMissing } from "../store/gone.js";
5
+ import { noteFiles } from "./files.js";
6
+ const MAX_TEXT = 200_000;
7
+ const ROOT = ".";
8
+ const MANIFEST = "manifest";
9
+ const MAX_DELETED_SHARE = 0.2;
10
+ const MIN_DELETIONS_CAPPED = 5;
11
+ /** The store's account for a folder of notes: its absolute path, so two vaults never mix. */
12
+ export const notesKey = (folder) => ({ provider: "notes", account: resolve(folder) });
13
+ const inside = (folder, path) => relative(folder, path).split(sep).join("/");
14
+ const chatOf = (id) => (dirname(id) === "." ? ROOT : dirname(id));
15
+ const noteMessage = (id, path, text, modified) => ({
16
+ id,
17
+ chatId: chatOf(id),
18
+ senderId: null,
19
+ senderName: null,
20
+ timestamp: modified,
21
+ editedAt: null,
22
+ text: `${basename(path).replace(/\.(md|txt)$/i, "")}\n\n${text.replace(/\r\n?/g, "\n")}`.slice(0, MAX_TEXT),
23
+ outgoing: true,
24
+ attachments: [],
25
+ replyTo: null,
26
+ forwardedFrom: null,
27
+ reactions: null,
28
+ });
29
+ /**
30
+ * Each note is one stored message of the `notes` provider: the folder is the account, each subfolder a
31
+ * chat, the path inside the folder the message id. A manifest in the store's sync state says what each
32
+ * file was when last stored, so an untouched file is not even read and an identical one is not saved; an
33
+ * edit keeps the old text as a revision, and a note gone from the folder (or newly ignored) loses its text.
34
+ */
35
+ export const importNotes = async (store, folder, { ignore = [], now = Date.now } = {}) => {
36
+ const root = resolve(folder);
37
+ const key = notesKey(root);
38
+ const seenAt = now();
39
+ const stored = await store.syncState(key, MANIFEST);
40
+ const before = stored === undefined ? undefined : JSON.parse(stored.value);
41
+ const after = {};
42
+ const changed = new Map();
43
+ const newest = new Map();
44
+ const files = noteFiles(root, ignore);
45
+ const listed = files.length;
46
+ for (const path of files) {
47
+ const id = inside(root, path);
48
+ const stat = statSync(path);
49
+ const modified = stat.mtime.toISOString();
50
+ const chat = chatOf(id);
51
+ if (modified > (newest.get(chat) ?? ""))
52
+ newest.set(chat, modified);
53
+ const stamp = `${stat.mtimeMs}:${stat.size}:`;
54
+ const previous = before?.[id];
55
+ if (previous?.startsWith(stamp)) {
56
+ after[id] = previous;
57
+ continue;
58
+ }
59
+ const text = readFileSync(path, "utf8");
60
+ const hash = createHash("sha256").update(text).digest("hex");
61
+ after[id] = `${stamp}${hash}`;
62
+ if (previous?.endsWith(`:${hash}`))
63
+ continue;
64
+ changed.set(chat, [...(changed.get(chat) ?? []), noteMessage(id, path, text, modified)]);
65
+ }
66
+ if (before === undefined)
67
+ await store.saveAccount(key, { name: basename(root) });
68
+ if (changed.size > 0) {
69
+ const chats = [...changed.keys()].map((id) => ({
70
+ id,
71
+ title: id === ROOT ? basename(root) : id,
72
+ kind: "saved",
73
+ unreadCount: null,
74
+ lastMessageAt: newest.get(id) ?? null,
75
+ participantsCount: null,
76
+ }));
77
+ await store.applyDelta(key, { chats });
78
+ for (const [chatId, notes] of changed)
79
+ await store.saveMessages(key, chatId, notes, { via: "cli-memo", seenAt });
80
+ }
81
+ // Before the first manifest, what is stored can only be learnt from the store itself.
82
+ const gone = before === undefined
83
+ ? await dropMissing(store, key, new Set(Object.keys(after)))
84
+ : await dropGone(store, key, before, after);
85
+ if (gone.skipped !== undefined && before !== undefined) {
86
+ // Kept in the manifest, so the next run weighs them again rather than forgetting them.
87
+ for (const id of Object.keys(before))
88
+ after[id] ??= before[id];
89
+ }
90
+ if (JSON.stringify(after) !== stored?.value)
91
+ await store.setSyncState(key, MANIFEST, JSON.stringify(after));
92
+ return {
93
+ folder: root,
94
+ notes: listed,
95
+ changed: [...changed.values()].reduce((sum, notes) => sum + notes.length, 0),
96
+ chats: [
97
+ ...new Set([
98
+ ...changed.keys(),
99
+ ...(gone.deleted > 0
100
+ ? Object.keys(before ?? {})
101
+ .filter((id) => !(id in after))
102
+ .map(chatOf)
103
+ : []),
104
+ ]),
105
+ ],
106
+ deleted: gone.deleted,
107
+ ...(gone.skipped === undefined ? {} : { deletionsSkipped: gone.skipped }),
108
+ };
109
+ };
110
+ /** Gone = in the manifest, not in the folder. A large share missing at once is refused, as for mail. */
111
+ const dropGone = async (store, key, before, after) => {
112
+ const gone = Object.keys(before).filter((id) => !(id in after));
113
+ if (gone.length === 0)
114
+ return { deleted: 0 };
115
+ const known = Object.keys(before).length;
116
+ if (gone.length >= MIN_DELETIONS_CAPPED && gone.length > known * MAX_DELETED_SHARE)
117
+ return {
118
+ deleted: 0,
119
+ skipped: `${gone.length} of ${known} stored notes are missing from the folder — too many to trust; nothing deleted`,
120
+ };
121
+ const byChat = new Map();
122
+ for (const id of gone)
123
+ byChat.set(chatOf(id), [...(byChat.get(chatOf(id)) ?? []), id]);
124
+ let deleted = 0;
125
+ for (const [chatId, ids] of byChat)
126
+ deleted += await store.markDeleted(key, ids, { chatId });
127
+ return { deleted };
128
+ };
129
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/notes/import.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export interface NoteAbout {
2
+ locator: string;
3
+ path: string;
4
+ modifiedAt: string;
5
+ aliases: string[];
6
+ }
7
+ export interface NoteMention {
8
+ locator: string;
9
+ path: string;
10
+ line: number;
11
+ text: string;
12
+ modifiedAt: string;
13
+ }
14
+ export interface NotesAnswer {
15
+ name: string;
16
+ about: NoteAbout[];
17
+ links: NoteMention[];
18
+ /** The plain name only: another person with the same name matches too. */
19
+ weak: NoteMention[];
20
+ /** More links or weak mentions were found than the limit let through. */
21
+ hasMore: boolean;
22
+ complete: boolean;
23
+ notRead: {
24
+ folder: string;
25
+ reason: string;
26
+ }[];
27
+ }
28
+ export declare const linkTargets: (line: string) => string[];
29
+ /** Reads only: never writes to a folder. A folder that cannot be read is reported, not fatal. */
30
+ export declare const findNotes: (folders: string[], name: string, limit?: number, ignore?: string[]) => NotesAnswer;
31
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/notes/read.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,0EAA0E;IAC1E,IAAI,EAAE,WAAW,EAAE,CAAA;IACnB,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC9C;AAiDD,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,MAAM,EACgC,CAAA;AAajF,iGAAiG;AACjG,eAAO,MAAM,SAAS,YAAa,MAAM,EAAE,QAAQ,MAAM,2BAAsB,MAAM,EAAE,KAAQ,WA+C9F,CAAA"}
@@ -0,0 +1,97 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import { basename, resolve } from "node:path";
3
+ import { parse } from "yaml";
4
+ import { noteFiles } from "./files.js";
5
+ const MAX_LINE = 200;
6
+ const WIKI_LINK = /\[\[([^\]|#^]+)(?:[#^][^\]|]*)?(?:\|[^\]]*)?\]\]/g;
7
+ const same = (a, b) => a.localeCompare(b, undefined, { sensitivity: "accent" }) === 0;
8
+ const frontMatter = (text) => {
9
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
10
+ if (match === null)
11
+ return { data: {}, bodyStart: 0 };
12
+ let data;
13
+ try {
14
+ data = parse(match[1] ?? "");
15
+ }
16
+ catch {
17
+ data = {};
18
+ }
19
+ const lines = match[0].split("\n").length - 1;
20
+ return { data: typeof data === "object" && data !== null ? data : {}, bodyStart: lines };
21
+ };
22
+ const aliasesOf = (data) => {
23
+ const value = data.aliases ?? data.alias;
24
+ if (typeof value === "string")
25
+ return [value];
26
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
27
+ };
28
+ const readNote = (path) => {
29
+ const text = readFileSync(path, "utf8");
30
+ return {
31
+ path,
32
+ title: basename(path).replace(/\.(md|txt)$/i, ""),
33
+ aliases: aliasesOf(frontMatter(text).data),
34
+ lines: text.split(/\r?\n/),
35
+ modifiedAt: statSync(path).mtime.toISOString(),
36
+ };
37
+ };
38
+ const literal = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
39
+ const plainName = (name) => new RegExp(`(?<![\\p{L}\\p{N}])${literal(name)}(?![\\p{L}\\p{N}])`, "iu");
40
+ export const linkTargets = (line) => [...line.matchAll(WIKI_LINK)].map((match) => basename((match[1] ?? "").trim()));
41
+ const mention = (note, index) => ({
42
+ locator: `note:${note.path}#L${index + 1}`,
43
+ path: note.path,
44
+ line: index + 1,
45
+ text: note.lines[index]?.trim().slice(0, MAX_LINE) ?? "",
46
+ modifiedAt: note.modifiedAt,
47
+ });
48
+ const newestFirst = (items) => items.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
49
+ /** Reads only: never writes to a folder. A folder that cannot be read is reported, not fatal. */
50
+ export const findNotes = (folders, name, limit = 20, ignore = []) => {
51
+ const notRead = [];
52
+ const notes = folders.flatMap((folder) => {
53
+ try {
54
+ return noteFiles(resolve(folder), ignore).map(readNote);
55
+ }
56
+ catch (error) {
57
+ notRead.push({ folder, reason: error instanceof Error ? error.message : String(error) });
58
+ return [];
59
+ }
60
+ });
61
+ const isAbout = (note) => [note.title, ...note.aliases].some((known) => same(known, name));
62
+ const aboutNotes = notes.filter(isAbout);
63
+ const names = [name, ...aboutNotes.flatMap((note) => [note.title, ...note.aliases])];
64
+ const plain = names.map(plainName);
65
+ const links = [];
66
+ const weak = [];
67
+ for (const note of notes) {
68
+ if (isAbout(note))
69
+ continue;
70
+ const { bodyStart } = frontMatter(note.lines.join("\n"));
71
+ note.lines.forEach((line, index) => {
72
+ if (index < bodyStart)
73
+ return;
74
+ if (linkTargets(line).some((target) => names.some((known) => same(known, target)))) {
75
+ links.push(mention(note, index));
76
+ }
77
+ else if (plain.some((pattern) => pattern.test(line.replace(WIKI_LINK, "")))) {
78
+ weak.push(mention(note, index));
79
+ }
80
+ });
81
+ }
82
+ return {
83
+ name,
84
+ about: newestFirst(aboutNotes.map((note) => ({
85
+ locator: `note:${note.path}`,
86
+ path: note.path,
87
+ modifiedAt: note.modifiedAt,
88
+ aliases: note.aliases,
89
+ }))),
90
+ links: newestFirst(links).slice(0, limit),
91
+ weak: newestFirst(weak).slice(0, limit),
92
+ hasMore: links.length > limit || weak.length > limit,
93
+ complete: notRead.length === 0,
94
+ notRead,
95
+ };
96
+ };
97
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/notes/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { MessageStore } from "@leemour/cli-messaging/store";
2
+ import type { NotesMap } from "../people/notes-map.js";
3
+ export interface NoteHit {
4
+ locator: string;
5
+ /** The note's path inside its folder. */
6
+ path: string;
7
+ folder: string;
8
+ modifiedAt: string;
9
+ /** The first line holding a word of the query. */
10
+ line: string;
11
+ /** How it was found: by meaning (its embedded chunks), by words, or both. */
12
+ by: ("meaning" | "words")[];
13
+ }
14
+ export interface LinkedPerson {
15
+ name: string;
16
+ /** How many of the notes found link them. */
17
+ notes: number;
18
+ /** The store's person, when `memo note` named this note as theirs. */
19
+ person: string | null;
20
+ }
21
+ /** A person the store knows whose full name appears in the notes found — by name only, so a guess. */
22
+ export interface MentionedPerson {
23
+ provider: string;
24
+ id: string;
25
+ name: string;
26
+ notes: number;
27
+ }
28
+ export interface NotesSearch {
29
+ query: string;
30
+ /** `words`: no folder was embedded or the model is not downloaded, so only words were searched. */
31
+ meaning: "searched" | "words";
32
+ hits: NoteHit[];
33
+ hasMore: boolean;
34
+ /** Who the notes found link with `[[Name]]`, most linked first: people pages, and other notes too. */
35
+ linked: LinkedPerson[];
36
+ /** Weak: the same name can belong to someone else. */
37
+ mentioned: MentionedPerson[];
38
+ }
39
+ export declare const searchNotes: (store: MessageStore, query: string, { limit, notesMap }?: {
40
+ limit?: number;
41
+ notesMap?: NotesMap;
42
+ }) => Promise<NotesSearch>;
43
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/notes/search.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAEhE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAGtD,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,EAAE,EAAE,CAAC,SAAS,GAAG,OAAO,CAAC,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,sGAAsG;AACtG,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,mGAAmG;IACnG,OAAO,EAAE,UAAU,GAAG,OAAO,CAAA;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,sGAAsG;IACtG,MAAM,EAAE,YAAY,EAAE,CAAA;IACtB,sDAAsD;IACtD,SAAS,EAAE,eAAe,EAAE,CAAA;CAC7B;AAoCD,eAAO,MAAM,WAAW,UACf,YAAY,SACZ,MAAM,wBACkB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CAAE,KACrE,OAAO,CAAC,WAAW,CAoDrB,CAAA"}
@@ -0,0 +1,83 @@
1
+ import { basename } from "node:path";
2
+ import { formatLocator } from "@leemour/cli-messaging";
3
+ import { embeddingsService, storeOnlyDeps } from "@leemour/cli-messaging/services";
4
+ import { APP } from "../app.js";
5
+ import { linkTargets } from "./read.js";
6
+ const MAX_LINE = 200;
7
+ /** Shorter names, or a single word, match too much ordinary text to mean anyone. */
8
+ const MIN_NAME = 5;
9
+ const literal = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
+ const knownPeople = async (store) => {
11
+ const providers = new Set((await store.accounts()).map(({ provider }) => provider).filter((p) => p !== "notes"));
12
+ const people = [];
13
+ for (const provider of providers) {
14
+ for (const contact of (await store.people(provider)).all()) {
15
+ const name = contact.name?.trim();
16
+ if (!name || name.length < MIN_NAME || !/\s/.test(name))
17
+ continue;
18
+ people.push({
19
+ provider,
20
+ id: contact.id,
21
+ name,
22
+ pattern: new RegExp(`(?<![\\p{L}\\p{N}])${literal(name)}(?![\\p{L}\\p{N}])`, "iu"),
23
+ });
24
+ }
25
+ }
26
+ return people;
27
+ };
28
+ const firstLine = (text, query) => {
29
+ const words = query
30
+ .toLowerCase()
31
+ .split(/\s+/)
32
+ .filter((word) => word.length > 2);
33
+ const lines = text.split("\n").slice(2);
34
+ const found = lines.find((line) => words.some((word) => line.toLowerCase().includes(word)));
35
+ return (found ?? lines.find((line) => line.trim() !== "") ?? "").trim().slice(0, MAX_LINE);
36
+ };
37
+ export const searchNotes = async (store, query, { limit = 20, notesMap = {} } = {}) => {
38
+ const folders = (await store.accounts()).filter(({ provider }) => provider === "notes");
39
+ const found = [];
40
+ let meaning = "words";
41
+ for (const key of folders) {
42
+ const answer = await embeddingsService(storeOnlyDeps(store, key, { app: APP })).search(query, { limit });
43
+ if (answer.meaning === "searched" && answer.hits.some(({ by }) => by.includes("meaning")))
44
+ meaning = "searched";
45
+ for (const hit of answer.hits)
46
+ found.push({
47
+ key,
48
+ id: hit.chunk.firstMessageId,
49
+ chatId: hit.summary.chatId,
50
+ by: hit.by,
51
+ score: hit.score ?? 0,
52
+ });
53
+ }
54
+ found.sort((a, b) => b.by.length - a.by.length || b.score - a.score);
55
+ const items = (await Promise.all(found.slice(0, limit).map(async ({ key, id, chatId, by }) => {
56
+ const message = await store.message(key, id, { chatId });
57
+ return message === undefined ? [] : [{ key, message, by }];
58
+ }))).flat();
59
+ const page = { items: items.map(({ message }) => message), hasMore: found.length > limit };
60
+ const hits = items.map(({ key, message, by }) => ({
61
+ locator: formatLocator({ provider: key.provider, account: key.account, chat: message.chatId, message: message.id }),
62
+ path: message.id,
63
+ folder: key.account,
64
+ modifiedAt: message.timestamp,
65
+ line: firstLine(message.text, query),
66
+ by,
67
+ }));
68
+ const personOfNote = new Map(Object.entries(notesMap).map(([uid, path]) => [basename(path).replace(/\.md$/i, ""), uid]));
69
+ const counts = new Map();
70
+ for (const hit of page.items) {
71
+ for (const name of new Set(hit.text.split("\n").flatMap(linkTargets)))
72
+ counts.set(name, (counts.get(name) ?? 0) + 1);
73
+ }
74
+ const linked = [...counts]
75
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
76
+ .map(([name, notes]) => ({ name, notes, person: personOfNote.get(name) ?? null }));
77
+ const mentioned = (await knownPeople(store))
78
+ .map(({ pattern, ...person }) => ({ ...person, notes: page.items.filter(({ text }) => pattern.test(text)).length }))
79
+ .filter(({ name, notes }) => notes > 0 && !counts.has(name))
80
+ .sort((a, b) => b.notes - a.notes || a.name.localeCompare(b.name));
81
+ return { query, meaning, hits, hasMore: page.hasMore, linked, mentioned };
82
+ };
83
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/notes/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { NotesAnswer } from "./read.js";
2
+ export declare const notesText: (answer: NotesAnswer) => string;
3
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/notes/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,WAAW,EAAE,MAAM,WAAW,CAAA;AAKzD,eAAO,MAAM,SAAS,WAAY,WAAW,KAAG,MAWlC,CAAA"}
@@ -0,0 +1,13 @@
1
+ const mentions = (title, items) => items.length === 0 ? [] : [title, ...items.map((item) => ` ${item.path}:${item.line} ${item.text}`), ""];
2
+ export const notesText = (answer) => [
3
+ ...(answer.about.length === 0
4
+ ? [`No note about ${answer.name}.`, ""]
5
+ : ["About", ...answer.about.map((note) => ` ${note.path}`), ""]),
6
+ ...mentions("Linked", answer.links),
7
+ ...mentions("Plain-name mentions (weak: may be someone else)", answer.weak),
8
+ ...(answer.hasMore ? ["More were found; raise --limit to see them.", ""] : []),
9
+ ...answer.notRead.map(({ folder, reason }) => `Not read: ${folder || "notes"} — ${reason}`),
10
+ ]
11
+ .join("\n")
12
+ .trimEnd();
13
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/notes/text.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,KAAoB,EAAY,EAAE,CACjE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;AAE5G,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAmB,EAAU,EAAE,CACvD;IACE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAC3B,CAAC,CAAC,CAAC,iBAAiB,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC;QACvC,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACnE,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC;IACnC,GAAG,QAAQ,CAAC,iDAAiD,EAAE,MAAM,CAAC,IAAI,CAAC;IAC3E,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,6CAA6C,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,aAAa,MAAM,IAAI,OAAO,MAAM,MAAM,EAAE,CAAC;CAC5F;KACE,IAAI,CAAC,IAAI,CAAC;KACV,OAAO,EAAE,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const positive: (value: string) => number;
2
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ,UAAW,MAAM,KAAG,MAIxC,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { InvalidArgumentError } from "commander";
2
+ export const positive = (value) => {
3
+ const number = Number(value);
4
+ if (!Number.isInteger(number) || number < 1)
5
+ throw new InvalidArgumentError("a whole number above zero");
6
+ return number;
7
+ };
8
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAEhD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAU,EAAE;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,oBAAoB,CAAC,2BAA2B,CAAC,CAAA;IACxG,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { type Streams } from "@leemour/cli-core";
2
+ import type { Command } from "commander";
3
+ /** Who is who is linked in messaging (`tg contacts link`); only the note about a person is ours. */
4
+ export declare const noteCommand: (program: Command, streams: Streams, env: NodeJS.ProcessEnv) => void;
5
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/people/command.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAIxC,oGAAoG;AACpG,eAAO,MAAM,WAAW,YAAa,OAAO,WAAW,OAAO,OAAO,MAAM,CAAC,UAAU,KAAG,IAqBxF,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { resolve } from "node:path";
2
+ import { CliError } from "@leemour/cli-core";
3
+ import { openStore } from "@leemour/cli-messaging/store";
4
+ import { identityOf } from "./identity.js";
5
+ import { loadNotesMap, saveNotesMap } from "./notes-map.js";
6
+ /** Who is who is linked in messaging (`tg contacts link`); only the note about a person is ours. */
7
+ export const noteCommand = (program, streams, env) => {
8
+ program
9
+ .command("note")
10
+ .description("Name the note that is about a person, or show it")
11
+ .argument("<identity>", "<messenger>:<name or id> of any identity of the person")
12
+ .argument("[path]", "the Markdown note about them")
13
+ .option("--clear", "forget the note")
14
+ .action(async (identity, path, options) => {
15
+ const store = await openStore({ env });
16
+ try {
17
+ const person = await store.personOf(await identityOf(store, identity));
18
+ if (person === undefined)
19
+ throw new CliError("not_found", `the store has no person for "${identity}"`);
20
+ const map = loadNotesMap(env);
21
+ if (options.clear)
22
+ delete map[person.uid];
23
+ else if (path !== undefined)
24
+ map[person.uid] = resolve(path);
25
+ if (options.clear || path !== undefined)
26
+ saveNotesMap(map, env);
27
+ streams.data(`${person.name ?? person.uid}: ${map[person.uid] ?? "no note"}\n`);
28
+ }
29
+ finally {
30
+ await store.close();
31
+ }
32
+ });
33
+ };
34
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/people/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAgB,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE3D,oGAAoG;AACpG,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAE,OAAgB,EAAE,GAAsB,EAAQ,EAAE;IAC9F,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,kDAAkD,CAAC;SAC/D,QAAQ,CAAC,YAAY,EAAE,wDAAwD,CAAC;SAChF,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,CAAC;SAClD,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC;SACpC,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,IAAwB,EAAE,OAA4B,EAAE,EAAE;QACzF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,MAAM,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;YACtE,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,gCAAgC,QAAQ,GAAG,CAAC,CAAA;YACtG,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;YAC7B,IAAI,OAAO,CAAC,KAAK;gBAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;iBACpC,IAAI,IAAI,KAAK,SAAS;gBAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;YAC5D,IAAI,OAAO,CAAC,KAAK,IAAI,IAAI,KAAK,SAAS;gBAAE,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAC/D,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,SAAS,IAAI,CAAC,CAAA;QACjF,CAAC;gBAAS,CAAC;YACT,MAAM,KAAK,CAAC,KAAK,EAAE,CAAA;QACrB,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { IdentityRef, MessageStore } from "@leemour/cli-messaging/store";
2
+ /** `telegram:Ana`, `max:12345`, `email:ana@example.com` — the messenger is always named. */
3
+ export declare const identityOf: (store: MessageStore, reference: string) => Promise<IdentityRef>;
4
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/people/identity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAE7E,4FAA4F;AAC5F,eAAO,MAAM,UAAU,UAAiB,YAAY,aAAa,MAAM,KAAG,OAAO,CAAC,WAAW,CAQ5F,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { CliError } from "@leemour/cli-core";
2
+ import { pickPerson } from "@leemour/cli-messaging";
3
+ /** `telegram:Ana`, `max:12345`, `email:ana@example.com` — the messenger is always named. */
4
+ export const identityOf = async (store, reference) => {
5
+ const match = /^([a-z][a-z0-9-]*):(.+)$/.exec(reference.trim());
6
+ if (match === null)
7
+ throw new CliError("validation_error", `"${reference}" names no messenger — write it as <messenger>:<name or id>`);
8
+ const [, provider = "", who = ""] = match;
9
+ const people = await store.people(provider);
10
+ const exact = people.get(provider === "email" ? who.toLowerCase() : who);
11
+ return { provider, id: exact?.id ?? pickPerson(who, people).id };
12
+ };
13
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/people/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGnD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,KAAmB,EAAE,SAAiB,EAAwB,EAAE;IAC/F,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAA;IAC/D,IAAI,KAAK,KAAK,IAAI;QAChB,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,IAAI,SAAS,6DAA6D,CAAC,CAAA;IACpH,MAAM,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,KAAK,CAAA;IACzC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACxE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAA;AAClE,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ import * as v from "valibot";
2
+ declare const NotesMapSchema: v.RecordSchema<v.StringSchema<undefined>, v.StringSchema<undefined>, undefined>;
3
+ /** Person uid → the note about them: the one link the store cannot hold (ruling NEED-596). */
4
+ export type NotesMap = v.InferOutput<typeof NotesMapSchema>;
5
+ export declare const loadNotesMap: (env?: NodeJS.ProcessEnv) => NotesMap;
6
+ export declare const saveNotesMap: (map: NotesMap, env?: NodeJS.ProcessEnv) => void;
7
+ export {};
8
+ //# sourceMappingURL=notes-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes-map.d.ts","sourceRoot":"","sources":["../../src/people/notes-map.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,QAAA,MAAM,cAAc,iFAAmC,CAAA;AAEvD,8FAA8F;AAC9F,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,cAAc,CAAC,CAAA;AAK3D,eAAO,MAAM,YAAY,SAAS,MAAM,CAAC,UAAU,KAAiB,QACb,CAAA;AAEvD,eAAO,MAAM,YAAY,QAAS,QAAQ,QAAO,MAAM,CAAC,UAAU,KAAiB,IACnD,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { join } from "node:path";
2
+ import { loadConfigFile, resolvePaths, saveConfigFile } from "@leemour/cli-core";
3
+ import * as v from "valibot";
4
+ const NotesMapSchema = v.record(v.string(), v.string());
5
+ const path = (env) => join(resolvePaths({ appName: "cli-memo", prefix: "MEMO", env }).config, "people-notes.json");
6
+ export const loadNotesMap = (env = process.env) => loadConfigFile(path(env), NotesMapSchema, () => ({}));
7
+ export const saveNotesMap = (map, env = process.env) => saveConfigFile(path(env), map);
8
+ //# sourceMappingURL=notes-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes-map.js","sourceRoot":"","sources":["../../src/people/notes-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAChF,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;AAKvD,MAAM,IAAI,GAAG,CAAC,GAAsB,EAAU,EAAE,CAC9C,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;AAE9F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAY,EAAE,CAC7E,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAa,EAAE,GAAG,GAAsB,OAAO,CAAC,GAAG,EAAQ,EAAE,CACxF,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { type Streams } from "@leemour/cli-core";
2
+ import { Command } from "commander";
3
+ export declare const createProgram: ({ streams, env, }?: {
4
+ streams?: Streams;
5
+ env?: NodeJS.ProcessEnv;
6
+ }) => Command;
7
+ //# sourceMappingURL=program.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AASnC,eAAO,MAAM,aAAa,uBAGvB;IACD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CACxB,KAAQ,OAaR,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { processStreams } from "@leemour/cli-core";
2
+ import { Command } from "commander";
3
+ import { contextCommand } from "./context/command.js";
4
+ import { mailCommand } from "./mail/command.js";
5
+ import { notesCommand } from "./notes/command.js";
6
+ import { noteCommand } from "./people/command.js";
7
+ import { autoCommand } from "./run/auto.js";
8
+ import { importCommand } from "./run/import.js";
9
+ import { VERSION } from "./version.js";
10
+ export const createProgram = ({ streams = processStreams, env = process.env, } = {}) => {
11
+ const program = new Command("memo")
12
+ .description("Context about people across messengers, notes and mail")
13
+ .version(VERSION);
14
+ contextCommand(program, streams, env);
15
+ notesCommand(program, streams, env);
16
+ mailCommand(program, streams, env);
17
+ noteCommand(program, streams, env);
18
+ importCommand(program, streams, env);
19
+ autoCommand(program, streams, env);
20
+ return program;
21
+ };
22
+ //# sourceMappingURL=program.js.map