@wirecat/cli-memo 0.0.0-stage → 0.5.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 (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +314 -2
  3. package/dist/answers/command.d.ts +35 -0
  4. package/dist/answers/command.d.ts.map +1 -0
  5. package/dist/answers/command.js +117 -0
  6. package/dist/answers/command.js.map +1 -0
  7. package/dist/app.d.ts +9 -0
  8. package/dist/app.d.ts.map +1 -0
  9. package/dist/app.js +10 -0
  10. package/dist/app.js.map +1 -0
  11. package/dist/bin/memo.d.ts +3 -0
  12. package/dist/bin/memo.d.ts.map +1 -0
  13. package/dist/bin/memo.js +14 -0
  14. package/dist/bin/memo.js.map +1 -0
  15. package/dist/config.d.ts +39 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +40 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/context/command.d.ts +6 -0
  20. package/dist/context/command.d.ts.map +1 -0
  21. package/dist/context/command.js +73 -0
  22. package/dist/context/command.js.map +1 -0
  23. package/dist/context/context.d.ts +28 -0
  24. package/dist/context/context.d.ts.map +1 -0
  25. package/dist/context/context.js +49 -0
  26. package/dist/context/context.js.map +1 -0
  27. package/dist/knowledge/command.d.ts +4 -0
  28. package/dist/knowledge/command.d.ts.map +1 -0
  29. package/dist/knowledge/command.js +158 -0
  30. package/dist/knowledge/command.js.map +1 -0
  31. package/dist/mail/command.d.ts +4 -0
  32. package/dist/mail/command.d.ts.map +1 -0
  33. package/dist/mail/command.js +61 -0
  34. package/dist/mail/command.js.map +1 -0
  35. package/dist/mail/gmail.d.ts +18 -0
  36. package/dist/mail/gmail.d.ts.map +1 -0
  37. package/dist/mail/gmail.js +103 -0
  38. package/dist/mail/gmail.js.map +1 -0
  39. package/dist/mail/himalaya.d.ts +4 -0
  40. package/dist/mail/himalaya.d.ts.map +1 -0
  41. package/dist/mail/himalaya.js +13 -0
  42. package/dist/mail/himalaya.js.map +1 -0
  43. package/dist/mail/import.d.ts +53 -0
  44. package/dist/mail/import.d.ts.map +1 -0
  45. package/dist/mail/import.js +170 -0
  46. package/dist/mail/import.js.map +1 -0
  47. package/dist/mail/parse.d.ts +21 -0
  48. package/dist/mail/parse.d.ts.map +1 -0
  49. package/dist/mail/parse.js +86 -0
  50. package/dist/mail/parse.js.map +1 -0
  51. package/dist/notes/about.d.ts +31 -0
  52. package/dist/notes/about.d.ts.map +1 -0
  53. package/dist/notes/about.js +69 -0
  54. package/dist/notes/about.js.map +1 -0
  55. package/dist/notes/bound.d.ts +24 -0
  56. package/dist/notes/bound.d.ts.map +1 -0
  57. package/dist/notes/bound.js +57 -0
  58. package/dist/notes/bound.js.map +1 -0
  59. package/dist/notes/command.d.ts +4 -0
  60. package/dist/notes/command.d.ts.map +1 -0
  61. package/dist/notes/command.js +222 -0
  62. package/dist/notes/command.js.map +1 -0
  63. package/dist/notes/dialects/common.d.ts +18 -0
  64. package/dist/notes/dialects/common.d.ts.map +1 -0
  65. package/dist/notes/dialects/common.js +79 -0
  66. package/dist/notes/dialects/common.js.map +1 -0
  67. package/dist/notes/dialects/index.d.ts +5 -0
  68. package/dist/notes/dialects/index.d.ts.map +1 -0
  69. package/dist/notes/dialects/index.js +6 -0
  70. package/dist/notes/dialects/index.js.map +1 -0
  71. package/dist/notes/dialects/markdown.d.ts +7 -0
  72. package/dist/notes/dialects/markdown.d.ts.map +1 -0
  73. package/dist/notes/dialects/markdown.js +51 -0
  74. package/dist/notes/dialects/markdown.js.map +1 -0
  75. package/dist/notes/dialects/obsidian.d.ts +6 -0
  76. package/dist/notes/dialects/obsidian.d.ts.map +1 -0
  77. package/dist/notes/dialects/obsidian.js +33 -0
  78. package/dist/notes/dialects/obsidian.js.map +1 -0
  79. package/dist/notes/dialects/types.d.ts +39 -0
  80. package/dist/notes/dialects/types.d.ts.map +1 -0
  81. package/dist/notes/dialects/types.js +2 -0
  82. package/dist/notes/dialects/types.js.map +1 -0
  83. package/dist/notes/embed.d.ts +51 -0
  84. package/dist/notes/embed.d.ts.map +1 -0
  85. package/dist/notes/embed.js +109 -0
  86. package/dist/notes/embed.js.map +1 -0
  87. package/dist/notes/export.d.ts +27 -0
  88. package/dist/notes/export.d.ts.map +1 -0
  89. package/dist/notes/export.js +76 -0
  90. package/dist/notes/export.js.map +1 -0
  91. package/dist/notes/files.d.ts +14 -0
  92. package/dist/notes/files.d.ts.map +1 -0
  93. package/dist/notes/files.js +77 -0
  94. package/dist/notes/files.js.map +1 -0
  95. package/dist/notes/folders-command.d.ts +4 -0
  96. package/dist/notes/folders-command.d.ts.map +1 -0
  97. package/dist/notes/folders-command.js +113 -0
  98. package/dist/notes/folders-command.js.map +1 -0
  99. package/dist/notes/folders.d.ts +27 -0
  100. package/dist/notes/folders.d.ts.map +1 -0
  101. package/dist/notes/folders.js +46 -0
  102. package/dist/notes/folders.js.map +1 -0
  103. package/dist/notes/import.d.ts +33 -0
  104. package/dist/notes/import.d.ts.map +1 -0
  105. package/dist/notes/import.js +183 -0
  106. package/dist/notes/import.js.map +1 -0
  107. package/dist/notes/internal.d.ts +17 -0
  108. package/dist/notes/internal.d.ts.map +1 -0
  109. package/dist/notes/internal.js +47 -0
  110. package/dist/notes/internal.js.map +1 -0
  111. package/dist/notes/links.d.ts +18 -0
  112. package/dist/notes/links.d.ts.map +1 -0
  113. package/dist/notes/links.js +44 -0
  114. package/dist/notes/links.js.map +1 -0
  115. package/dist/notes/renames.d.ts +11 -0
  116. package/dist/notes/renames.d.ts.map +1 -0
  117. package/dist/notes/renames.js +24 -0
  118. package/dist/notes/renames.js.map +1 -0
  119. package/dist/notes/search.d.ts +5 -0
  120. package/dist/notes/search.d.ts.map +1 -0
  121. package/dist/notes/search.js +20 -0
  122. package/dist/notes/search.js.map +1 -0
  123. package/dist/notes/state.d.ts +14 -0
  124. package/dist/notes/state.d.ts.map +1 -0
  125. package/dist/notes/state.js +21 -0
  126. package/dist/notes/state.js.map +1 -0
  127. package/dist/options.d.ts +4 -0
  128. package/dist/options.d.ts.map +1 -0
  129. package/dist/options.js +12 -0
  130. package/dist/options.js.map +1 -0
  131. package/dist/people/command.d.ts +5 -0
  132. package/dist/people/command.d.ts.map +1 -0
  133. package/dist/people/command.js +57 -0
  134. package/dist/people/command.js.map +1 -0
  135. package/dist/people/identity.d.ts +4 -0
  136. package/dist/people/identity.d.ts.map +1 -0
  137. package/dist/people/identity.js +13 -0
  138. package/dist/people/identity.js.map +1 -0
  139. package/dist/people/notes-map.d.ts +13 -0
  140. package/dist/people/notes-map.d.ts.map +1 -0
  141. package/dist/people/notes-map.js +49 -0
  142. package/dist/people/notes-map.js.map +1 -0
  143. package/dist/people/subject.d.ts +8 -0
  144. package/dist/people/subject.d.ts.map +1 -0
  145. package/dist/people/subject.js +19 -0
  146. package/dist/people/subject.js.map +1 -0
  147. package/dist/program.d.ts +7 -0
  148. package/dist/program.d.ts.map +1 -0
  149. package/dist/program.js +34 -0
  150. package/dist/program.js.map +1 -0
  151. package/dist/run/auto.d.ts +20 -0
  152. package/dist/run/auto.d.ts.map +1 -0
  153. package/dist/run/auto.js +139 -0
  154. package/dist/run/auto.js.map +1 -0
  155. package/dist/run/import.d.ts +18 -0
  156. package/dist/run/import.d.ts.map +1 -0
  157. package/dist/run/import.js +114 -0
  158. package/dist/run/import.js.map +1 -0
  159. package/dist/search/command.d.ts +38 -0
  160. package/dist/search/command.d.ts.map +1 -0
  161. package/dist/search/command.js +269 -0
  162. package/dist/search/command.js.map +1 -0
  163. package/dist/store/gone.d.ts +17 -0
  164. package/dist/store/gone.d.ts.map +1 -0
  165. package/dist/store/gone.js +52 -0
  166. package/dist/store/gone.js.map +1 -0
  167. package/dist/store/scope.d.ts +19 -0
  168. package/dist/store/scope.d.ts.map +1 -0
  169. package/dist/store/scope.js +34 -0
  170. package/dist/store/scope.js.map +1 -0
  171. package/dist/store/source-target.d.ts +18 -0
  172. package/dist/store/source-target.d.ts.map +1 -0
  173. package/dist/store/source-target.js +28 -0
  174. package/dist/store/source-target.js.map +1 -0
  175. package/dist/tags/command.d.ts +4 -0
  176. package/dist/tags/command.d.ts.map +1 -0
  177. package/dist/tags/command.js +166 -0
  178. package/dist/tags/command.js.map +1 -0
  179. package/dist/tasks/command.d.ts +4 -0
  180. package/dist/tasks/command.d.ts.map +1 -0
  181. package/dist/tasks/command.js +83 -0
  182. package/dist/tasks/command.js.map +1 -0
  183. package/dist/tasks/context.d.ts +16 -0
  184. package/dist/tasks/context.d.ts.map +1 -0
  185. package/dist/tasks/context.js +68 -0
  186. package/dist/tasks/context.js.map +1 -0
  187. package/dist/version.d.ts +2 -0
  188. package/dist/version.d.ts.map +1 -0
  189. package/dist/version.js +2 -0
  190. package/dist/version.js.map +1 -0
  191. package/package.json +53 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian.js","sourceRoot":"","sources":["../../../src/notes/dialects/obsidian.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What a note says, in no editor's syntax. A dialect turns a file into this and back; nothing above
3
+ * the dialect ever sees `[[…]]` or `[…](…)`.
4
+ */
5
+ export interface NoteLink {
6
+ /**
7
+ * A note as written, extension dropped (`People/Rin`, `../Rin`), or a typed reference
8
+ * (`person:…`, `msg:…`) when the link names a record instead of a file.
9
+ */
10
+ target: string;
11
+ /** `#Heading` or `#^block`, when the link points inside the note. */
12
+ anchor: string | null;
13
+ label: string | null;
14
+ }
15
+ export interface ParsedNote {
16
+ title: string;
17
+ aliases: string[];
18
+ /** As written, without `#`; a nested Obsidian tag keeps its `/`. */
19
+ tags: string[];
20
+ links: NoteLink[];
21
+ frontMatter: Record<string, unknown>;
22
+ }
23
+ export interface NoteForExport {
24
+ title: string;
25
+ text: string;
26
+ aliases: string[];
27
+ tags: string[];
28
+ /** Links the text does not already carry; written after it. */
29
+ links: NoteLink[];
30
+ frontMatter: Record<string, unknown>;
31
+ }
32
+ export declare const DIALECTS: readonly ["obsidian", "markdown"];
33
+ export type DialectName = (typeof DIALECTS)[number];
34
+ export interface NoteDialect {
35
+ name: DialectName;
36
+ parse(text: string, path: string): ParsedNote;
37
+ render(note: NoteForExport): string;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/notes/dialects/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,oEAAoE;IACpE,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,+DAA+D;IAC/D,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAED,eAAO,MAAM,QAAQ,YAAI,UAAU,EAAE,UAAU,CAAU,CAAA;AACzD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;IAC7C,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAA;CACpC"}
@@ -0,0 +1,2 @@
1
+ export const DIALECTS = ["obsidian", "markdown"];
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/notes/dialects/types.ts"],"names":[],"mappings":"AAkCA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,UAAU,CAAU,CAAA"}
@@ -0,0 +1,51 @@
1
+ import type { AccountKey, MessageStore } from "@wirecat/cli-messaging/store";
2
+ /** About three chunks a second with e5-small on this laptop (2026-10-06): a run stays near three minutes, under a five-minute timer, and the next resumes. */
3
+ export declare const EMBED_PER_RUN = 600;
4
+ export interface NotesEmbedded {
5
+ chats: number;
6
+ chunks: number;
7
+ /** Chunks left for the next run, when the run's bound stopped it. */
8
+ left: boolean;
9
+ /** Why nothing was embedded, when nothing could be. */
10
+ notEmbedded?: string;
11
+ pendingChats?: string[];
12
+ }
13
+ /**
14
+ * Builds each chat's conversations (one per note) — what search by words and by meaning both read — and,
15
+ * unless `embed` is off, embeds the chunks that have no vector yet. Chunks are
16
+ * keyed by their text's hash, so an unchanged note is never embedded twice. A missing model is reported,
17
+ * never downloaded.
18
+ */
19
+ export declare const embedNotes: (store: MessageStore, key: AccountKey, chats: string[], { maxChunks, embed, env, }?: {
20
+ maxChunks?: number;
21
+ embed?: boolean;
22
+ env?: NodeJS.ProcessEnv;
23
+ }) => Promise<NotesEmbedded>;
24
+ /**
25
+ * Embeds what changed since the last run, and whatever an earlier run left. The folders waiting are kept in
26
+ * the store's sync state, so a run stopped by its bound, or a missing model, picks up next time; the first run
27
+ * after embedding exists takes every folder already stored.
28
+ */
29
+ export declare const embedChanged: (store: MessageStore, key: AccountKey, changed: string[], options?: {
30
+ maxChunks?: number;
31
+ maxChats?: number;
32
+ embed?: boolean;
33
+ env?: NodeJS.ProcessEnv;
34
+ }) => Promise<NotesEmbedded>;
35
+ export interface NoteChunksEmbedded {
36
+ chunks: number;
37
+ /** Chunks left for the next run, when the run's bound stopped it. */
38
+ left: boolean;
39
+ /** Why nothing was embedded, when nothing could be. */
40
+ notEmbedded?: string;
41
+ }
42
+ /**
43
+ * Embeds the notes' chunks that have no vector yet, for search by meaning. Bounded like mail, so a
44
+ * timer run stays short and the next continues. A missing model is reported, never downloaded.
45
+ */
46
+ export declare const embedNoteChunks: (store: MessageStore, { maxChunks, embed, env, }?: {
47
+ maxChunks?: number;
48
+ embed?: boolean;
49
+ env?: NodeJS.ProcessEnv;
50
+ }) => Promise<NoteChunksEmbedded>;
51
+ //# sourceMappingURL=embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../../src/notes/embed.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAG5E,8JAA8J;AAC9J,eAAO,MAAM,aAAa,MAAM,CAAA;AAEhC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,IAAI,EAAE,OAAO,CAAA;IACb,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAKD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,UACd,YAAY,OACd,UAAU,SACR,MAAM,EAAE,+BAKZ;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAClE,OAAO,CAAC,aAAa,CA4CvB,CAAA;AAID;;;;GAIG;AACH,eAAO,MAAM,YAAY,UAChB,YAAY,OACd,UAAU,WACN,MAAM,EAAE,YACR;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAC3F,OAAO,CAAC,aAAa,CAoBvB,CAAA;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,IAAI,EAAE,OAAO,CAAA;IACb,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,UACnB,YAAY,+BAKhB;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAClE,OAAO,CAAC,kBAAkB,CAa5B,CAAA"}
@@ -0,0 +1,109 @@
1
+ import { CliError } from "@wirecat/cli-core";
2
+ import { conversationsService, embeddingsService, embedNotes as embedNoteChunksOf, storeOnlyDeps, } from "@wirecat/cli-messaging/services";
3
+ import { APP } from "../app.js";
4
+ /** About three chunks a second with e5-small on this laptop (2026-10-06): a run stays near three minutes, under a five-minute timer, and the next resumes. */
5
+ export const EMBED_PER_RUN = 600;
6
+ const isMissingModel = (error) => error instanceof CliError && error.code === "not_found" && /is not downloaded/.test(error.message);
7
+ /**
8
+ * Builds each chat's conversations (one per note) — what search by words and by meaning both read — and,
9
+ * unless `embed` is off, embeds the chunks that have no vector yet. Chunks are
10
+ * keyed by their text's hash, so an unchanged note is never embedded twice. A missing model is reported,
11
+ * never downloaded.
12
+ */
13
+ export const embedNotes = async (store, key, chats, { maxChunks = EMBED_PER_RUN, embed = true, env, } = {}) => {
14
+ const deps = storeOnlyDeps(store, key, { app: APP, ...(env === undefined ? {} : { env }) });
15
+ const conversations = conversationsService(deps);
16
+ const embeddings = embeddingsService(deps);
17
+ for (const chat of chats) {
18
+ await conversations.build(chat).catch((error) => {
19
+ // A folder whose every note was deleted has nothing left to build.
20
+ if (!(error instanceof CliError && error.code === "not_found"))
21
+ throw error;
22
+ });
23
+ }
24
+ let chunks = 0;
25
+ let left = false;
26
+ const pendingChats = [];
27
+ for (const [index, chat] of (embed ? chats : []).entries()) {
28
+ if (chunks >= maxChunks) {
29
+ left = true;
30
+ pendingChats.push(...chats.slice(index));
31
+ break;
32
+ }
33
+ try {
34
+ const done = await embeddings.embed(chat, { maxChunks: maxChunks - chunks });
35
+ chunks += done.embedded;
36
+ if ((await embeddings.status(chat)).left > 0) {
37
+ left = true;
38
+ pendingChats.push(chat);
39
+ }
40
+ }
41
+ catch (error) {
42
+ if (!isMissingModel(error))
43
+ throw error;
44
+ return {
45
+ chats: chats.length,
46
+ chunks,
47
+ left: true,
48
+ notEmbedded: "the e5-small model is not downloaded — tg models text download e5-small",
49
+ pendingChats: [...pendingChats, ...chats.slice(index)],
50
+ };
51
+ }
52
+ }
53
+ return {
54
+ chats: chats.length,
55
+ chunks,
56
+ left,
57
+ ...(pendingChats.length ? { pendingChats } : {}),
58
+ ...(embed ? {} : { notEmbedded: "embedding is off", pendingChats: chats }),
59
+ };
60
+ };
61
+ const PENDING = "embed_pending";
62
+ /**
63
+ * Embeds what changed since the last run, and whatever an earlier run left. The folders waiting are kept in
64
+ * the store's sync state, so a run stopped by its bound, or a missing model, picks up next time; the first run
65
+ * after embedding exists takes every folder already stored.
66
+ */
67
+ export const embedChanged = async (store, key, changed, options = {}) => {
68
+ const saved = await store.syncState(key, PENDING);
69
+ const waiting = saved === undefined
70
+ ? (await store.chats(key, { limit: 10_000 })).items.map(({ id }) => id)
71
+ : JSON.parse(saved.value);
72
+ // A build under older rules, or one a change made stale, is rebuilt even when no note changed since.
73
+ const deps = storeOnlyDeps(store, key, { app: APP, ...(options.env === undefined ? {} : { env: options.env }) });
74
+ const behind = (await embeddingsService(deps).readiness({})).chats
75
+ .filter(({ state, graph, vectors }) => state === "stale" || graph?.outdatedRules === true || vectors.missing > 0)
76
+ .map(({ chat }) => chat);
77
+ const chats = [...new Set([...changed, ...waiting, ...behind])];
78
+ if (chats.length === 0)
79
+ return { chats: 0, chunks: 0, left: false };
80
+ const selected = chats.slice(0, options.maxChats ?? 100);
81
+ const result = await embedNotes(store, key, selected, options);
82
+ const pending = [...chats.slice(selected.length), ...(result.pendingChats ?? (result.left ? selected : []))];
83
+ result.left ||= pending.length > 0;
84
+ // Embedding off leaves every chat waiting, so turning it on later embeds them.
85
+ await store.setSyncState(key, PENDING, JSON.stringify(pending));
86
+ return result;
87
+ };
88
+ /**
89
+ * Embeds the notes' chunks that have no vector yet, for search by meaning. Bounded like mail, so a
90
+ * timer run stays short and the next continues. A missing model is reported, never downloaded.
91
+ */
92
+ export const embedNoteChunks = async (store, { maxChunks = EMBED_PER_RUN, embed = true, env, } = {}) => {
93
+ if (!embed)
94
+ return { chunks: 0, left: true, notEmbedded: "embedding is off" };
95
+ try {
96
+ const done = await embedNoteChunksOf(store, { maxChunks, command: "tg", ...(env === undefined ? {} : { env }) });
97
+ return { chunks: done.embedded, left: done.left };
98
+ }
99
+ catch (error) {
100
+ if (!isMissingModel(error))
101
+ throw error;
102
+ return {
103
+ chunks: 0,
104
+ left: true,
105
+ notEmbedded: "the e5-small model is not downloaded — tg models text download e5-small",
106
+ };
107
+ }
108
+ };
109
+ //# sourceMappingURL=embed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.js","sourceRoot":"","sources":["../../src/notes/embed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { NoteDialect, NoteForExport } from "./dialects/index.js";
2
+ export interface ExportedNote extends NoteForExport {
3
+ /** The note's id in the store; written as `memo-id` so import skips the file. */
4
+ id: string;
5
+ }
6
+ export interface ExportResult {
7
+ dir: string;
8
+ written: string[];
9
+ unchanged: string[];
10
+ /** Files memo wrote that were edited since: left as they are, so the edit is not lost. */
11
+ edited: {
12
+ path: string;
13
+ id: string;
14
+ }[];
15
+ }
16
+ export declare const editedSinceExport: (content: string) => boolean;
17
+ /**
18
+ * Writes each note to `dir` in `dialect`'s format. A file keeps its name across exports (found by its
19
+ * `memo-id`), is rewritten only while it is still what memo wrote, and a file memo did not write is never
20
+ * touched. Nothing is deleted.
21
+ */
22
+ export declare const exportNotes: ({ dir, dialect, notes, }: {
23
+ dir: string;
24
+ dialect: NoteDialect;
25
+ notes: ExportedNote[];
26
+ }) => ExportResult;
27
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/notes/export.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGrE,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,0FAA0F;IAC1F,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACvC;AAkBD,eAAO,MAAM,iBAAiB,YAAa,MAAM,KAAG,OAGnD,CAAA;AAkBD;;;;GAIG;AACH,eAAO,MAAM,WAAW,6BAIrB;IACD,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,WAAW,CAAA;IACpB,KAAK,EAAE,YAAY,EAAE,CAAA;CACtB,KAAG,YAkCH,CAAA"}
@@ -0,0 +1,76 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { readMemoId } from "./files.js";
5
+ const HASH_LINE = /^memo-hash: .*\r?\n/m;
6
+ const PLACEHOLDER = "f".repeat(64);
7
+ const UNSAFE = /[\\/:*?"<>|#^[\]\p{Cc}]/gu;
8
+ const MAX_NAME = 120;
9
+ const sha256 = (text) => createHash("sha256").update(text).digest("hex");
10
+ /**
11
+ * The hash covers the file minus its own `memo-hash` line, so a file can prove it is still what memo
12
+ * wrote without memo keeping any record of its own.
13
+ */
14
+ const withHash = (content) => {
15
+ const hash = sha256(content.replace(HASH_LINE, ""));
16
+ return content.replace(`memo-hash: ${PLACEHOLDER}`, `memo-hash: ${hash}`);
17
+ };
18
+ export const editedSinceExport = (content) => {
19
+ const stored = /^memo-hash: ([0-9a-f]{64})$/m.exec(content)?.[1];
20
+ return stored === undefined || sha256(content.replace(HASH_LINE, "")) !== stored;
21
+ };
22
+ const fileName = (title, id, taken) => {
23
+ const base = title.replace(UNSAFE, " ").replace(/\s+/g, " ").trim().slice(0, MAX_NAME) || id;
24
+ const plain = `${base}.md`;
25
+ return taken(plain) ? `${base} (${id.slice(-8)}).md` : plain;
26
+ };
27
+ const exportedIn = (dir) => {
28
+ const found = new Map();
29
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
30
+ if (!entry.isFile() || !/\.(md|markdown)$/i.test(entry.name))
31
+ continue;
32
+ const id = readMemoId(join(dir, entry.name));
33
+ if (id !== null)
34
+ found.set(id, entry.name);
35
+ }
36
+ return found;
37
+ };
38
+ /**
39
+ * Writes each note to `dir` in `dialect`'s format. A file keeps its name across exports (found by its
40
+ * `memo-id`), is rewritten only while it is still what memo wrote, and a file memo did not write is never
41
+ * touched. Nothing is deleted.
42
+ */
43
+ export const exportNotes = ({ dir, dialect, notes, }) => {
44
+ mkdirSync(dir, { recursive: true });
45
+ const existing = exportedIn(dir);
46
+ const claimed = new Set(existing.values());
47
+ const result = { dir, written: [], unchanged: [], edited: [] };
48
+ for (const { id, ...note } of notes) {
49
+ const frontMatter = { "memo-id": id, "memo-hash": PLACEHOLDER, ...note.frontMatter };
50
+ frontMatter["memo-id"] = id;
51
+ frontMatter["memo-hash"] = PLACEHOLDER;
52
+ const content = withHash(dialect.render({ ...note, frontMatter }));
53
+ const known = existing.get(id);
54
+ const name = known ?? fileName(note.title, id, (candidate) => claimed.has(candidate) || existsSync(join(dir, candidate)));
55
+ claimed.add(name);
56
+ const path = join(dir, name);
57
+ if (known !== undefined) {
58
+ const current = readFileSync(path, "utf8");
59
+ if (editedSinceExport(current)) {
60
+ result.edited.push({ path, id });
61
+ continue;
62
+ }
63
+ if (current === content) {
64
+ result.unchanged.push(path);
65
+ continue;
66
+ }
67
+ }
68
+ // Hidden, so an import running meanwhile never lists a half-written file.
69
+ const partial = join(dir, `.${name}.memo-tmp`);
70
+ writeFileSync(partial, content);
71
+ renameSync(partial, path);
72
+ result.written.push(path);
73
+ }
74
+ return result;
75
+ };
76
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/notes/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare const NOTE_EXTENSIONS: string[];
2
+ /** The `memo-id` of a file memo exported, read from the front matter at its start; `null` for any other file. */
3
+ export declare const readMemoId: (path: string) => string | null;
4
+ export declare const isExported: (path: string) => boolean;
5
+ /**
6
+ * Note files under `folder`, as absolute paths. Hidden entries (`.obsidian`, `.trash`) are skipped, and
7
+ * so is whatever an `ignore` rule names — a path inside the folder (`Psychology/Personal Notes.md`,
8
+ * `Archive`) or a glob (`**\/Private*`, `*.txt`). A note memo exported is skipped too: it is already in
9
+ * the store, and importing it would make it a second note.
10
+ */
11
+ export declare const noteFiles: (folder: string, ignore?: string[], extensions?: string[], { skipExported }?: {
12
+ skipExported?: boolean;
13
+ }) => string[];
14
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/notes/files.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,UAe3B,CAAA;AAKD,iHAAiH;AACjH,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,MAAM,GAAG,IAUlD,CAAA;AAID,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,OAA4D,CAAA;AAoBtG;;;;;GAKG;AACH,eAAO,MAAM,SAAS,WACZ,MAAM,WACN,MAAM,EAAE,4CAES;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,KAClD,MAAM,EAgBR,CAAA"}
@@ -0,0 +1,77 @@
1
+ import { closeSync, openSync, readdirSync, readSync } from "node:fs";
2
+ import { join, relative, sep } from "node:path";
3
+ export const NOTE_EXTENSIONS = [
4
+ ".md",
5
+ ".markdown",
6
+ ".txt",
7
+ ".csv",
8
+ ".tsv",
9
+ ".pdf",
10
+ ".docx",
11
+ ".xlsx",
12
+ ".odt",
13
+ ".ods",
14
+ ".pptx",
15
+ ".epub",
16
+ ".doc",
17
+ ".xls",
18
+ ];
19
+ const MEMO_ID = /^---\r?\n(?:[^\n]*\n)*?memo-id:[ \t]*["']?([^"'\s]+)/;
20
+ const HEAD = 1024;
21
+ /** The `memo-id` of a file memo exported, read from the front matter at its start; `null` for any other file. */
22
+ export const readMemoId = (path) => {
23
+ const buffer = Buffer.alloc(HEAD);
24
+ const descriptor = openSync(path, "r");
25
+ try {
26
+ const head = buffer.subarray(0, readSync(descriptor, buffer, 0, HEAD, 0)).toString("utf8");
27
+ const end = head.indexOf("\n---", 3);
28
+ return MEMO_ID.exec(end < 0 ? head : head.slice(0, end + 1))?.[1] ?? null;
29
+ }
30
+ finally {
31
+ closeSync(descriptor);
32
+ }
33
+ };
34
+ const TEXT_NOTE = /\.(md|markdown)$/i;
35
+ export const isExported = (path) => TEXT_NOTE.test(path) && readMemoId(path) !== null;
36
+ const pattern = (rule) => {
37
+ const glob = rule.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
38
+ const body = glob
39
+ .split(/(\*\*\/?|\*|\?)/)
40
+ .map((part) => part === "**/" || part === "**"
41
+ ? ".*"
42
+ : part === "*"
43
+ ? "[^/]*"
44
+ : part === "?"
45
+ ? "[^/]"
46
+ : part.replace(/[.+^${}()|[\]\\]/g, "\\$&"))
47
+ .join("");
48
+ // A rule names a path inside the folder, and everything under it when it is a folder.
49
+ return new RegExp(`^${body}(/.*)?$`, "i");
50
+ };
51
+ /**
52
+ * Note files under `folder`, as absolute paths. Hidden entries (`.obsidian`, `.trash`) are skipped, and
53
+ * so is whatever an `ignore` rule names — a path inside the folder (`Psychology/Personal Notes.md`,
54
+ * `Archive`) or a glob (`**\/Private*`, `*.txt`). A note memo exported is skipped too: it is already in
55
+ * the store, and importing it would make it a second note.
56
+ */
57
+ export const noteFiles = (folder, ignore = [], extensions = NOTE_EXTENSIONS, { skipExported = true } = {}) => {
58
+ const rules = ignore.map(pattern);
59
+ const ignored = (path) => {
60
+ const inside = relative(folder, path).split(sep).join("/");
61
+ return rules.some((rule) => rule.test(inside));
62
+ };
63
+ const walk = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
64
+ if (entry.name.startsWith("."))
65
+ return [];
66
+ const path = join(dir, entry.name);
67
+ if (ignored(path))
68
+ return [];
69
+ if (entry.isDirectory())
70
+ return walk(path);
71
+ if (!entry.isFile() || !extensions.some((ext) => entry.name.toLowerCase().endsWith(ext)))
72
+ return [];
73
+ return skipExported && isExported(path) ? [] : [path];
74
+ });
75
+ return walk(folder);
76
+ };
77
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/notes/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Streams } from "@wirecat/cli-core";
2
+ import { type Command } from "commander";
3
+ export declare const foldersCommand: (program: Command, streams: Streams, env: NodeJS.ProcessEnv) => void;
4
+ //# sourceMappingURL=folders-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"folders-command.d.ts","sourceRoot":"","sources":["../../src/notes/folders-command.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE1E,OAAO,EAAE,KAAK,OAAO,EAAwB,MAAM,WAAW,CAAA;AAwB9D,eAAO,MAAM,cAAc,YAAa,OAAO,WAAW,OAAO,OAAO,MAAM,CAAC,UAAU,KAAG,IA0F3F,CAAA"}
@@ -0,0 +1,113 @@
1
+ import { statSync } from "node:fs";
2
+ import { basename, resolve } from "node:path";
3
+ import { CliError, createRenderer } from "@wirecat/cli-core";
4
+ import { openStore } from "@wirecat/cli-messaging/store";
5
+ import { InvalidArgumentError } from "commander";
6
+ import { loadConfig, updateConfig } from "../config.js";
7
+ import { claimPendingFolders } from "./bound.js";
8
+ import { DIALECTS } from "./dialects/index.js";
9
+ import { attachFolder, bindFolder, noteFolders } from "./folders.js";
10
+ const formatOf = (value) => {
11
+ if (!DIALECTS.includes(value))
12
+ throw new InvalidArgumentError(`choose ${DIALECTS.join(", ")}`);
13
+ return value;
14
+ };
15
+ const directory = (path) => {
16
+ const at = resolve(path);
17
+ let isDirectory = false;
18
+ try {
19
+ isDirectory = statSync(at).isDirectory();
20
+ }
21
+ catch { }
22
+ if (!isDirectory)
23
+ throw new CliError("not_found", `${at} is not a folder`);
24
+ return at;
25
+ };
26
+ const line = (folder) => `${folder.id ?? "(no id — memo folders add <path>)"} ${folder.format} ${folder.path}`;
27
+ export const foldersCommand = (program, streams, env) => {
28
+ const print = (json, value, text) => {
29
+ if (json)
30
+ createRenderer({ format: "json", color: false, streams }).result(value);
31
+ else
32
+ streams.data(`${text}\n`);
33
+ };
34
+ const withStore = async (work) => {
35
+ const store = await openStore({ env });
36
+ try {
37
+ await claimPendingFolders(store, env);
38
+ return await work(store);
39
+ }
40
+ finally {
41
+ await store.close();
42
+ }
43
+ };
44
+ const folders = program
45
+ .command("folders")
46
+ .description("Folders of notes: an id that links and the store use, and where the folder is on this computer");
47
+ folders
48
+ .command("add")
49
+ .description("Give a folder of notes an id, or show the one it has")
50
+ .argument("<path>", "the folder on this computer")
51
+ .option("--format <format>", `how its notes are written: ${DIALECTS.join(", ")} (default: obsidian)`, formatOf)
52
+ .option("--json", "print JSON")
53
+ .action(async (path, options) => {
54
+ const at = directory(path);
55
+ const answer = await withStore(async (store) => {
56
+ const known = new Set((await store.notes.folders()).map(({ id }) => id));
57
+ const current = noteFolders(loadConfig(env).notes).find((folder) => folder.path === at);
58
+ if (current?.id && known.has(current.id)) {
59
+ const folder = { ...current, id: current.id, format: options.format ?? current.format };
60
+ if (folder.format !== current.format)
61
+ updateConfig((config) => bindFolder(config, folder), env);
62
+ return { ...folder, created: false };
63
+ }
64
+ const format = options.format ?? current?.format ?? "obsidian";
65
+ const made = await store.notes.addFolder({ name: basename(at) || at, format });
66
+ const folder = { id: made.id, path: at, format };
67
+ updateConfig((config) => bindFolder(config, folder), env);
68
+ return { ...folder, created: true };
69
+ });
70
+ print(options.json, answer, answer.id);
71
+ });
72
+ folders
73
+ .command("attach")
74
+ .description("Bind a folder id from another computer to the folder's path on this one")
75
+ .argument("<id>", "the folder's id, as memo folders add printed it")
76
+ .argument("<path>", "the same folder on this computer")
77
+ .option("--format <format>", `how its notes are written: ${DIALECTS.join(", ")}`, formatOf)
78
+ .option("--json", "print JSON")
79
+ .action(async (id, path, options) => {
80
+ const at = directory(path);
81
+ const folder = await withStore(async (store) => {
82
+ const stored = (await store.notes.folders()).find((candidate) => candidate.id === id);
83
+ if (!stored)
84
+ throw new CliError("not_found", `the store has no folder ${id} — memo folders list shows those it has`);
85
+ let bound;
86
+ updateConfig((config) => {
87
+ const result = attachFolder(config, id, at, options.format ?? stored.format);
88
+ bound = result.folder;
89
+ return result.config;
90
+ }, env);
91
+ return bound;
92
+ });
93
+ print(options.json, folder, line(folder));
94
+ });
95
+ folders
96
+ .command("list")
97
+ .description("The folders of notes: those on this computer, and those the store has from another")
98
+ .option("--json", "print JSON")
99
+ .action(async (options) => {
100
+ const answer = await withStore(async (store) => {
101
+ const here = noteFolders(loadConfig(env).notes);
102
+ const elsewhere = (await store.notes.folders())
103
+ .filter((folder) => !here.some((entry) => entry.id === folder.id))
104
+ .map(({ id, name, format }) => ({ id, name, format }));
105
+ return { items: here, elsewhere };
106
+ });
107
+ print(options.json, answer, [
108
+ ...answer.items.map(line),
109
+ ...answer.elsewhere.map(({ id, name }) => `${id} not on this computer (${name}) — memo folders attach ${id} <path>`),
110
+ ].join("\n") || "no folders: memo folders add <path>");
111
+ });
112
+ };
113
+ //# sourceMappingURL=folders-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"folders-command.js","sourceRoot":"","sources":["../../src/notes/folders-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAgB,MAAM,mBAAmB,CAAA;AAC1E,OAAO,EAAqB,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC3E,OAAO,EAAgB,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAoB,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAmB,WAAW,EAAE,MAAM,cAAc,CAAA;AAErF,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAe,EAAE;IAC9C,IAAI,CAAE,QAA8B,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,oBAAoB,CAAC,UAAU,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACrH,OAAO,KAAoB,CAAA;AAC7B,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,CAAC,IAAY,EAAU,EAAE;IACzC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,IAAI,WAAW,GAAG,KAAK,CAAA;IACvB,IAAI,CAAC;QACH,WAAW,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;IAC1C,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE,kBAAkB,CAAC,CAAA;IAC1E,OAAO,EAAE,CAAA;AACX,CAAC,CAAA;AAED,MAAM,IAAI,GAAG,CAAC,MAAkB,EAAE,EAAE,CAClC,GAAG,MAAM,CAAC,EAAE,IAAI,mCAAmC,KAAK,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,EAAE,CAAA;AAEzF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAE,OAAgB,EAAE,GAAsB,EAAQ,EAAE;IACjG,MAAM,KAAK,GAAG,CAAC,IAAyB,EAAE,KAAc,EAAE,IAAY,EAAE,EAAE;QACxE,IAAI,IAAI;YAAE,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;;YAC5E,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,CAAA;IAChC,CAAC,CAAA;IACD,MAAM,SAAS,GAAG,KAAK,EAAK,IAAyC,EAAc,EAAE;QACnF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;QACtC,IAAI,CAAC;YACH,MAAM,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;YACrC,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,CAAA;QAC1B,CAAC;gBAAS,CAAC;YACT,MAAM,KAAK,CAAC,KAAK,EAAE,CAAA;QACrB,CAAC;IACH,CAAC,CAAA;IACD,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,gGAAgG,CAAC,CAAA;IAEhH,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,sDAAsD,CAAC;SACnE,QAAQ,CAAC,QAAQ,EAAE,6BAA6B,CAAC;SACjD,MAAM,CAAC,mBAAmB,EAAE,8BAA8B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,QAAQ,CAAC;SAC9G,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC;SAC9B,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,OAAiD,EAAE,EAAE;QAChF,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YACxE,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,CAAA;YACvF,IAAI,OAAO,EAAE,EAAE,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAA;gBACvF,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;oBAAE,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAA;gBAC/F,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;YACtC,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,EAAE,MAAM,IAAI,UAAU,CAAA;YAC9D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;YAC9E,MAAM,MAAM,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAA;YAChD,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAA;YACzD,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACrC,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;IACxC,CAAC,CAAC,CAAA;IAEJ,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,yEAAyE,CAAC;SACtF,QAAQ,CAAC,MAAM,EAAE,iDAAiD,CAAC;SACnE,QAAQ,CAAC,QAAQ,EAAE,kCAAkC,CAAC;SACtD,MAAM,CAAC,mBAAmB,EAAE,8BAA8B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC;SAC1F,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC;SAC9B,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,IAAY,EAAE,OAAiD,EAAE,EAAE;QAC5F,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC7C,MAAM,MAAM,GAAG,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;YACrF,IAAI,CAAC,MAAM;gBACT,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,2BAA2B,EAAE,yCAAyC,CAAC,CAAA;YACzG,IAAI,KAA6B,CAAA;YACjC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;gBACtB,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,CAAA;gBAC5E,KAAK,GAAG,MAAM,CAAC,MAAM,CAAA;gBACrB,OAAO,MAAM,CAAC,MAAM,CAAA;YACtB,CAAC,EAAE,GAAG,CAAC,CAAA;YACP,OAAO,KAAmB,CAAA;QAC5B,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEJ,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,oFAAoF,CAAC;SACjG,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC;SAC9B,MAAM,CAAC,KAAK,EAAE,OAA2B,EAAE,EAAE;QAC5C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC7C,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;YAC/C,MAAM,SAAS,GAAG,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;iBAC5C,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC;iBACjE,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;YACxD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;QACnC,CAAC,CAAC,CAAA;QACF,KAAK,CACH,OAAO,CAAC,IAAI,EACZ,MAAM,EACN;YACE,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YACzB,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CACrB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,2BAA2B,IAAI,2BAA2B,EAAE,SAAS,CAC7F;SACF,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,qCAAqC,CACtD,CAAA;IACH,CAAC,CAAC,CAAA;AACN,CAAC,CAAA"}
@@ -0,0 +1,27 @@
1
+ import type { Config } from "../config.js";
2
+ import type { DialectName } from "./dialects/index.js";
3
+ /**
4
+ * A folder of notes on this computer. The id is what the store and every link use; the path is only
5
+ * where the folder is here, so the same folder can live elsewhere on another computer.
6
+ */
7
+ export interface NoteFolder {
8
+ /** `null` for a folder still written as a bare path, until `memo folders add` gives it an id. */
9
+ id: string | null;
10
+ path: string;
11
+ format: DialectName;
12
+ }
13
+ export declare const noteFolders: (notes: Config["notes"]) => NoteFolder[];
14
+ export declare const folderPaths: (notes: Config["notes"]) => string[];
15
+ /** Writes `folder` at its path, in place of whatever entry had that path or that id. */
16
+ export declare const bindFolder: (config: Config, folder: NoteFolder & {
17
+ id: string;
18
+ }) => Config;
19
+ /**
20
+ * Binds a folder id made elsewhere to its path on this computer. An id already here moves to the new
21
+ * path; a path already bound to another id is refused, since its notes would change identity.
22
+ */
23
+ export declare const attachFolder: (config: Config, id: string, path: string, format?: DialectName) => {
24
+ config: Config;
25
+ folder: NoteFolder;
26
+ };
27
+ //# sourceMappingURL=folders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"folders.d.ts","sourceRoot":"","sources":["../../src/notes/folders.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,iGAAiG;IACjG,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,CAAA;CACpB;AASD,eAAO,MAAM,WAAW,UAAW,MAAM,CAAC,OAAO,CAAC,KAAG,UAAU,EAAyC,CAAA;AAExG,eAAO,MAAM,WAAW,UAAW,MAAM,CAAC,OAAO,CAAC,KAAG,MAAM,EAAgD,CAAA;AAgB3G,wFAAwF;AACxF,eAAO,MAAM,UAAU,WAAY,MAAM,UAAU,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,KAAG,MAShF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,WACf,MAAM,MACV,MAAM,QACJ,MAAM,WACH,WAAW,KACnB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAatC,CAAA"}