@batalabs/virlow-mcp-core 3.101.0 → 3.102.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.
@@ -0,0 +1,79 @@
1
+ import type { VirlowApi } from './api.js';
2
+ import { type Repeat, type Task, type TodoList } from './todo-format.js';
3
+ import type { Vault } from './vault.js';
4
+ export interface ListTodosArgs {
5
+ list?: string;
6
+ include_completed?: boolean;
7
+ query?: string;
8
+ }
9
+ export interface AddTodoArgs {
10
+ text: string;
11
+ list?: string;
12
+ due?: string;
13
+ my_day?: boolean;
14
+ important?: boolean;
15
+ note?: string;
16
+ steps?: string[];
17
+ repeat?: Repeat;
18
+ remind_at?: string;
19
+ }
20
+ export interface UpdateTodoArgs {
21
+ id: string;
22
+ text?: string;
23
+ done?: boolean;
24
+ due?: string | null;
25
+ my_day?: boolean;
26
+ important?: boolean;
27
+ note?: string;
28
+ add_steps?: string[];
29
+ complete_steps?: string[];
30
+ list?: string | null;
31
+ repeat?: Repeat | null;
32
+ remind_at?: string | null;
33
+ }
34
+ export interface CreateTodoListArgs {
35
+ name: string;
36
+ emoji?: string;
37
+ color?: string;
38
+ }
39
+ interface Loaded {
40
+ tasks: Task[];
41
+ lists: TodoList[];
42
+ }
43
+ export declare class TodoService {
44
+ private readonly api;
45
+ private readonly vault;
46
+ /** The clock, for "today" and reminder checks. Tests pass a fixed one. */
47
+ private readonly now;
48
+ constructor(api: VirlowApi, vault: Vault,
49
+ /** The clock, for "today" and reminder checks. Tests pass a fixed one. */
50
+ now?: () => Date);
51
+ private today;
52
+ /** The key, or the vault's own "ask the user to unlock" error. */
53
+ private key;
54
+ private fetchAll;
55
+ private decrypt;
56
+ load(): Promise<Loaded>;
57
+ /** A list by id or name (case-insensitive). Never guesses between two
58
+ * lists with one name, and never makes one up. */
59
+ private resolveList;
60
+ private scopeOf;
61
+ private listName;
62
+ private describe;
63
+ listTodos(args: ListTodosArgs): Promise<string>;
64
+ private scopeName;
65
+ listLists(): Promise<string>;
66
+ private validDay;
67
+ private validRemind;
68
+ private encrypted;
69
+ private createTask;
70
+ private channels;
71
+ /** Cancels the task's pending reminders. */
72
+ private clearReminders;
73
+ /** No label: the email only says there is a reminder, since the text is encrypted. */
74
+ private createReminder;
75
+ addTodo(args: AddTodoArgs): Promise<string>;
76
+ updateTodo(args: UpdateTodoArgs): Promise<string>;
77
+ createList(args: CreateTodoListArgs): Promise<string>;
78
+ }
79
+ export {};
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TodoService = void 0;
4
+ const virlow_crypto_1 = require("@batalabs/virlow-crypto");
5
+ const todo_format_js_1 = require("./todo-format.js");
6
+ /**
7
+ * The to-do list over the API, for the MCP tools.
8
+ *
9
+ * Tasks and lists are notes the API never lists with the rest (types `todo`
10
+ * and `todolist`), encrypted like any note: the text and every detail are in
11
+ * the ciphertext, and the server sees only whether a task is done and, through
12
+ * a reminder, when to send one. The format is todo-format.ts, shared with the
13
+ * web app and mirrored by iOS, so a task written here reads the same there.
14
+ *
15
+ * Nothing is cached: a to-do list is small, and the apps change it all the
16
+ * time, so every call reads the current state.
17
+ */
18
+ /** Smart list names as a tool takes them. */
19
+ const SMART_ALIASES = {
20
+ my_day: 'myday',
21
+ myday: 'myday',
22
+ 'my day': 'myday',
23
+ important: 'important',
24
+ planned: 'planned',
25
+ tasks: 'tasks',
26
+ };
27
+ const DAY = /^\d{4}-\d{2}-\d{2}$/;
28
+ const HEX = /^#[0-9a-f]{6}$/i;
29
+ const NOTE_PREVIEW = 200;
30
+ class TodoService {
31
+ api;
32
+ vault;
33
+ now;
34
+ constructor(api, vault,
35
+ /** The clock, for "today" and reminder checks. Tests pass a fixed one. */
36
+ now = () => new Date()) {
37
+ this.api = api;
38
+ this.vault = vault;
39
+ this.now = now;
40
+ }
41
+ today() {
42
+ return (0, todo_format_js_1.dayKey)(this.now());
43
+ }
44
+ /** The key, or the vault's own "ask the user to unlock" error. */
45
+ key() {
46
+ return this.vault.session.key;
47
+ }
48
+ // MARK: Reading
49
+ async fetchAll(type) {
50
+ const rows = [];
51
+ for (let page = 1;; page++) {
52
+ const result = await this.api.listNotes({ type, deleted: false, limit: 50, page });
53
+ rows.push(...result.notes);
54
+ if (page >= result.pagination.pages || result.notes.length === 0)
55
+ break;
56
+ }
57
+ // Filtered here too: an API that predates the type ignores the filter.
58
+ return rows.filter((row) => row.type === type && row.deleted !== true);
59
+ }
60
+ async decrypt(key, row) {
61
+ if (!row.encryptedTitle)
62
+ return { title: row.title, content: row.content === '[ENCRYPTED]' ? '' : row.content };
63
+ try {
64
+ return await (0, virlow_crypto_1.decryptNoteFields)(key, row.encryptedTitle, row.encryptedContent ?? '', row.iv ?? '');
65
+ }
66
+ catch {
67
+ // A row that will not open (another key, a damaged write) is left out
68
+ // rather than failing the whole list.
69
+ return null;
70
+ }
71
+ }
72
+ async load() {
73
+ const key = this.key();
74
+ const [taskRows, listRows] = await Promise.all([this.fetchAll(todo_format_js_1.TODO_NOTE_TYPE), this.fetchAll(todo_format_js_1.TODO_LIST_NOTE_TYPE)]);
75
+ const tasks = [];
76
+ for (const row of taskRows) {
77
+ const opened = await this.decrypt(key, row);
78
+ if (!opened)
79
+ continue;
80
+ tasks.push({
81
+ id: row.id,
82
+ title: opened.title,
83
+ details: (0, todo_format_js_1.parseDetails)(opened.content),
84
+ done: row.archived === true,
85
+ createdAt: row.createdAt,
86
+ updatedAt: row.updatedAt,
87
+ });
88
+ }
89
+ const lists = [];
90
+ for (const row of listRows) {
91
+ const opened = await this.decrypt(key, row);
92
+ if (!opened)
93
+ continue;
94
+ lists.push({ id: row.id, name: opened.title, meta: (0, todo_format_js_1.parseListMeta)(opened.content), createdAt: row.createdAt });
95
+ }
96
+ lists.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
97
+ return { tasks, lists };
98
+ }
99
+ /** A list by id or name (case-insensitive). Never guesses between two
100
+ * lists with one name, and never makes one up. */
101
+ resolveList(lists, ref) {
102
+ const byId = lists.find((l) => l.id === ref);
103
+ if (byId)
104
+ return byId;
105
+ const wanted = ref.trim().toLowerCase();
106
+ const named = lists.filter((l) => l.name.trim().toLowerCase() === wanted);
107
+ if (named.length === 1)
108
+ return named[0];
109
+ if (named.length > 1) {
110
+ throw new Error(`More than one list is called "${ref}". Use its id: ${named.map((l) => l.id).join(', ')}.`);
111
+ }
112
+ const known = lists.length ? lists.map((l) => `"${l.name}"`).join(', ') : 'none yet';
113
+ throw new Error(`No to-do list called "${ref}". The person's lists: ${known}. ` +
114
+ 'Use one of those, leave list out for Tasks, or call create_todo_list if they asked for a new one.');
115
+ }
116
+ scopeOf(lists, ref) {
117
+ if (ref === undefined || ref.trim() === '')
118
+ return null;
119
+ const smart = SMART_ALIASES[ref.trim().toLowerCase()];
120
+ if (smart)
121
+ return smart;
122
+ return (0, todo_format_js_1.listKey)(this.resolveList(lists, ref).id);
123
+ }
124
+ listName(lists, id) {
125
+ if (id === null)
126
+ return 'Tasks';
127
+ return lists.find((l) => l.id === id)?.name ?? 'Tasks';
128
+ }
129
+ describe(task, lists) {
130
+ const d = task.details;
131
+ const today = this.today();
132
+ const parts = [`list: ${this.listName(lists, d.list)}`];
133
+ if (d.due)
134
+ parts.push(`due ${d.due}${!task.done && d.due < today ? ' (overdue)' : ''}`);
135
+ if (d.myDay === today)
136
+ parts.push('in My Day');
137
+ if (d.important)
138
+ parts.push('important');
139
+ if (d.repeat)
140
+ parts.push(`repeats ${d.repeat}`);
141
+ if (d.remindAt)
142
+ parts.push(`remind at ${d.remindAt}`);
143
+ if (task.done && d.completedAt)
144
+ parts.push(`completed ${d.completedAt}`);
145
+ const lines = [`- [${task.done ? 'x' : ' '}] ${task.title} (id: ${task.id})`, ` ${parts.join(' · ')}`];
146
+ if (d.steps.length) {
147
+ const done = d.steps.filter((s) => s.done).length;
148
+ const steps = d.steps.map((s) => `[${s.done ? 'x' : ' '}] ${s.text} (id: ${s.id})`).join('; ');
149
+ lines.push(` steps ${done} of ${d.steps.length}: ${steps}`);
150
+ }
151
+ if (d.note.trim()) {
152
+ const note = d.note.trim().replace(/\s+/g, ' ');
153
+ lines.push(` note: ${note.length > NOTE_PREVIEW ? `${note.slice(0, NOTE_PREVIEW)}…` : note}`);
154
+ }
155
+ return lines.join('\n');
156
+ }
157
+ async listTodos(args) {
158
+ const { tasks, lists } = await this.load();
159
+ const today = this.today();
160
+ const scope = this.scopeOf(lists, args.list);
161
+ const query = args.query?.trim().toLowerCase();
162
+ const matches = (t) => !query || t.title.toLowerCase().includes(query) || t.details.note.toLowerCase().includes(query);
163
+ const open = scope
164
+ ? (0, todo_format_js_1.openTasks)(tasks, scope, today)
165
+ : tasks.filter((t) => !t.done).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
166
+ const done = args.include_completed
167
+ ? scope
168
+ ? (0, todo_format_js_1.completedTasks)(tasks, scope, today)
169
+ : tasks
170
+ .filter((t) => t.done)
171
+ .sort((a, b) => (b.details.completedAt ?? b.updatedAt).localeCompare(a.details.completedAt ?? a.updatedAt))
172
+ : [];
173
+ const shown = [...open.filter(matches), ...done.filter(matches)];
174
+ const where = scope ? this.scopeName(lists, scope) : 'every list';
175
+ if (shown.length === 0) {
176
+ return query
177
+ ? `No to-dos in ${where} match "${args.query}".`
178
+ : `No ${args.include_completed ? '' : 'open '}to-dos in ${where}.`;
179
+ }
180
+ const head = `${open.filter(matches).length} open${args.include_completed ? `, ${done.filter(matches).length} completed` : ''} in ${where} (today is ${today}):`;
181
+ return [head, ...shown.map((t) => this.describe(t, lists))].join('\n');
182
+ }
183
+ scopeName(lists, scope) {
184
+ const smart = todo_format_js_1.SMART_LISTS.find((s) => s.id === scope);
185
+ if (smart)
186
+ return smart.label;
187
+ return this.listName(lists, scope.slice(5));
188
+ }
189
+ async listLists() {
190
+ const { tasks, lists } = await this.load();
191
+ const today = this.today();
192
+ const lines = todo_format_js_1.SMART_LISTS.map((s) => `- ${s.label} (${s.id === 'myday' ? 'my_day' : s.id}): ${(0, todo_format_js_1.openTasks)(tasks, s.id, today).length} open`);
193
+ lines.push(...lists.map((l) => `- ${l.meta.emoji ? `${l.meta.emoji} ` : ''}${l.name} (id: ${l.id}): ${(0, todo_format_js_1.openTasks)(tasks, (0, todo_format_js_1.listKey)(l.id), today).length} open`));
194
+ return [`Smart lists, then the person's own lists (today is ${today}):`, ...lines].join('\n');
195
+ }
196
+ // MARK: Writing
197
+ validDay(value, field) {
198
+ if (!DAY.test(value) || Number.isNaN(new Date(`${value}T00:00:00`).getTime())) {
199
+ throw new Error(`${field} must be a day as YYYY-MM-DD, got "${value}".`);
200
+ }
201
+ return value;
202
+ }
203
+ validRemind(value) {
204
+ const at = new Date(value);
205
+ if (Number.isNaN(at.getTime()))
206
+ throw new Error(`remind_at must be an ISO 8601 date and time, got "${value}".`);
207
+ if (at.getTime() <= this.now().getTime())
208
+ throw new Error(`remind_at must be in the future, got ${value}.`);
209
+ return at;
210
+ }
211
+ async encrypted(title, content) {
212
+ const key = this.key();
213
+ const salt = (0, virlow_crypto_1.bytesToBase64)(crypto.getRandomValues(new Uint8Array(32)));
214
+ const { encryptedTitle, encryptedContent, iv } = await (0, virlow_crypto_1.encryptNoteFields)(key, title, content);
215
+ return { title: '[ENCRYPTED]', content: '[ENCRYPTED]', encryptedTitle, encryptedContent, iv, salt };
216
+ }
217
+ async createTask(text, details) {
218
+ return this.api.createNote({ ...(await this.encrypted(text, (0, todo_format_js_1.serializeDetails)(details))), type: todo_format_js_1.TODO_NOTE_TYPE, folderId: null });
219
+ }
220
+ async channels() {
221
+ try {
222
+ const integrations = await this.api.integrations();
223
+ return integrations.slack.connected ? ['email', 'slack'] : ['email'];
224
+ }
225
+ catch {
226
+ return ['email'];
227
+ }
228
+ }
229
+ /** Cancels the task's pending reminders. */
230
+ async clearReminders(noteId) {
231
+ const reminders = await this.api.listReminders(noteId);
232
+ for (const r of reminders) {
233
+ if (r.status === 'pending')
234
+ await this.api.deleteReminder(r.id);
235
+ }
236
+ }
237
+ /** No label: the email only says there is a reminder, since the text is encrypted. */
238
+ async createReminder(noteId, at) {
239
+ await this.api.createReminder({ noteId, remindAt: at.toISOString(), label: null, channels: await this.channels() });
240
+ }
241
+ async addTodo(args) {
242
+ const text = args.text?.trim();
243
+ if (!text)
244
+ throw new Error('text is required.');
245
+ if (args.repeat !== undefined && !todo_format_js_1.REPEATS.includes(args.repeat)) {
246
+ throw new Error(`repeat must be one of ${todo_format_js_1.REPEATS.join(', ')}.`);
247
+ }
248
+ const remindAt = args.remind_at !== undefined ? this.validRemind(args.remind_at) : null;
249
+ const { lists } = await this.load();
250
+ const today = this.today();
251
+ const details = (0, todo_format_js_1.parseDetails)('');
252
+ if (args.list !== undefined && args.list.trim() !== '')
253
+ details.list = this.resolveList(lists, args.list).id;
254
+ if (args.due !== undefined)
255
+ details.due = this.validDay(args.due, 'due');
256
+ if (args.my_day)
257
+ details.myDay = today;
258
+ if (args.important)
259
+ details.important = true;
260
+ if (args.note)
261
+ details.note = args.note;
262
+ if (args.steps)
263
+ details.steps = args.steps.map((s) => s.trim()).filter(Boolean).map((s) => ({ id: (0, todo_format_js_1.newId)(), text: s, done: false }));
264
+ if (args.repeat) {
265
+ details.repeat = args.repeat;
266
+ // A repeat needs a day to count from, as in the apps.
267
+ details.due ??= today;
268
+ }
269
+ if (remindAt)
270
+ details.remindAt = remindAt.toISOString();
271
+ const row = await this.createTask(text, details);
272
+ if (remindAt)
273
+ await this.createReminder(row.id, remindAt);
274
+ const task = { id: row.id, title: text, details, done: false, createdAt: row.createdAt, updatedAt: row.updatedAt };
275
+ return `Added to-do (id: ${row.id}).\n${this.describe(task, lists)}`;
276
+ }
277
+ async updateTodo(args) {
278
+ const { tasks, lists } = await this.load();
279
+ const task = tasks.find((t) => t.id === args.id);
280
+ if (!task)
281
+ throw new Error(`No to-do with id ${args.id}. Call list_todos to find it.`);
282
+ const today = this.today();
283
+ const details = { ...task.details, steps: task.details.steps.map((s) => ({ ...s })) };
284
+ let text = task.title;
285
+ if (args.text !== undefined) {
286
+ if (!args.text.trim())
287
+ throw new Error('text cannot be empty.');
288
+ text = args.text.trim();
289
+ }
290
+ if (args.due !== undefined)
291
+ details.due = args.due === null ? null : this.validDay(args.due, 'due');
292
+ if (args.my_day !== undefined)
293
+ details.myDay = args.my_day ? today : null;
294
+ if (args.important !== undefined)
295
+ details.important = args.important;
296
+ if (args.note !== undefined)
297
+ details.note = args.note;
298
+ if (args.list !== undefined)
299
+ details.list = args.list === null || args.list.trim() === '' ? null : this.resolveList(lists, args.list).id;
300
+ if (args.repeat !== undefined) {
301
+ if (args.repeat !== null && !todo_format_js_1.REPEATS.includes(args.repeat))
302
+ throw new Error(`repeat must be one of ${todo_format_js_1.REPEATS.join(', ')}.`);
303
+ details.repeat = args.repeat;
304
+ if (args.repeat)
305
+ details.due ??= today;
306
+ }
307
+ if (args.add_steps) {
308
+ details.steps.push(...args.add_steps.map((s) => s.trim()).filter(Boolean).map((s) => ({ id: (0, todo_format_js_1.newId)(), text: s, done: false })));
309
+ }
310
+ if (args.complete_steps) {
311
+ for (const ref of args.complete_steps) {
312
+ const step = details.steps.find((s) => s.id === ref) ?? details.steps.find((s) => s.text.trim() === ref.trim());
313
+ if (!step) {
314
+ const known = details.steps.map((s) => `"${s.text}"`).join(', ') || 'none';
315
+ throw new Error(`No step "${ref}" on this to-do. Its steps: ${known}.`);
316
+ }
317
+ step.done = true;
318
+ }
319
+ }
320
+ // The reminder: a new time replaces any pending one; null cancels it.
321
+ let remindAt;
322
+ if (args.remind_at !== undefined) {
323
+ remindAt = args.remind_at === null ? null : this.validRemind(args.remind_at);
324
+ details.remindAt = remindAt ? remindAt.toISOString() : null;
325
+ }
326
+ const completing = args.done === true && !task.done;
327
+ const reopening = args.done === false && task.done;
328
+ if (completing)
329
+ details.completedAt = this.now().toISOString();
330
+ if (reopening)
331
+ details.completedAt = null;
332
+ const done = completing ? true : reopening ? false : task.done;
333
+ const body = await this.encrypted(text, (0, todo_format_js_1.serializeDetails)(details));
334
+ if (completing || reopening)
335
+ body.archived = done;
336
+ await this.api.updateNote(task.id, body);
337
+ if (remindAt !== undefined) {
338
+ await this.clearReminders(task.id);
339
+ if (remindAt)
340
+ await this.createReminder(task.id, remindAt);
341
+ }
342
+ const lines = [];
343
+ if (completing) {
344
+ // A done task needs no reminder.
345
+ await this.clearReminders(task.id);
346
+ const next = (0, todo_format_js_1.nextRepeat)({ ...task, title: text, details }, today);
347
+ if (next) {
348
+ const at = next.remindAt ? new Date(next.remindAt) : null;
349
+ const future = at !== null && at.getTime() > this.now().getTime();
350
+ const nextDetails = { ...next, remindAt: future ? next.remindAt : null };
351
+ const row = await this.createTask(text, nextDetails);
352
+ if (future)
353
+ await this.createReminder(row.id, at);
354
+ lines.push(`It repeats ${next.repeat}: the next one (id: ${row.id}) is due ${next.due}.`);
355
+ }
356
+ }
357
+ const updated = { ...task, title: text, details, done };
358
+ return [`Updated to-do.`, this.describe(updated, lists), ...lines].join('\n');
359
+ }
360
+ async createList(args) {
361
+ const name = args.name?.trim();
362
+ if (!name)
363
+ throw new Error('name is required.');
364
+ if (args.color !== undefined && !HEX.test(args.color))
365
+ throw new Error(`color must be a hex color like #5c7fb5, got "${args.color}".`);
366
+ const { lists } = await this.load();
367
+ if (lists.some((l) => l.name.trim().toLowerCase() === name.toLowerCase())) {
368
+ throw new Error(`There is already a list called "${name}".`);
369
+ }
370
+ const meta = (0, todo_format_js_1.parseListMeta)('');
371
+ if (args.color)
372
+ meta.color = args.color.toLowerCase();
373
+ if (args.emoji?.trim())
374
+ meta.emoji = args.emoji.trim();
375
+ const row = await this.api.createNote({
376
+ ...(await this.encrypted(name, (0, todo_format_js_1.serializeListMeta)(meta))),
377
+ type: todo_format_js_1.TODO_LIST_NOTE_TYPE,
378
+ folderId: null,
379
+ });
380
+ return `Created the list "${name}" (id: ${row.id}).`;
381
+ }
382
+ }
383
+ exports.TodoService = TodoService;
@@ -47,6 +47,11 @@ export declare const TOOL_ANNOTATIONS: {
47
47
  readonly move_attachment: VirlowToolAnnotations;
48
48
  readonly delete_attachment: VirlowToolAnnotations;
49
49
  readonly restore_attachment: VirlowToolAnnotations;
50
+ readonly list_todos: VirlowToolAnnotations;
51
+ readonly list_todo_lists: VirlowToolAnnotations;
52
+ readonly add_todo: VirlowToolAnnotations;
53
+ readonly update_todo: VirlowToolAnnotations;
54
+ readonly create_todo_list: VirlowToolAnnotations;
50
55
  };
51
56
  export type ToolName = keyof typeof TOOL_ANNOTATIONS;
52
57
  /**
@@ -41,6 +41,8 @@ exports.toolConfig = toolConfig;
41
41
  * - unlock and lock change session state, not data, so neither is read-only
42
42
  * and neither is destructive. lock only discards this session's in-memory
43
43
  * key and caches; nothing stored is touched and unlock restores access.
44
+ * - update_todo is destructive: new text, a note or a list replaces what was
45
+ * there. add_todo and create_todo_list only add.
44
46
  * - read_attachment depends on the host. Without file access it only returns
45
47
  * the file. With it (local CLI, desktop app) `save_to` writes to the user's
46
48
  * disk and `overwrite: true` replaces an existing file, so it is a
@@ -110,6 +112,12 @@ exports.TOOL_ANNOTATIONS = {
110
112
  move_attachment: destructive('Move an attachment'),
111
113
  delete_attachment: destructive('Delete an attachment'),
112
114
  restore_attachment: additive('Restore an attachment'),
115
+ // to-do
116
+ list_todos: read('List to-dos'),
117
+ list_todo_lists: read('List to-do lists'),
118
+ add_todo: additive('Add a to-do'),
119
+ update_todo: destructive('Update a to-do'),
120
+ create_todo_list: additive('Create a to-do list'),
113
121
  };
114
122
  /**
115
123
  * A tool's annotations for this host. Only read_attachment varies: with file
package/dist/index.d.ts CHANGED
@@ -22,6 +22,8 @@ export { ATTRS_MAX_BYTES, TAGS_PROP, decodeAttrs, decodeSchema, defineProp, empt
22
22
  export type { FolderSchemaV1, NoteAttrsV1, PropDefinition, PropOption, PropType, PropValue, TagsValue } from './attrs.js';
23
23
  export { buildTitleMatcher, extractLinkIds, htmlToText } from './attrs-extract.js';
24
24
  export type { TitleEntry, TitleMatcher } from './attrs-extract.js';
25
+ export { TodoService } from './todos.js';
26
+ export type { AddTodoArgs, CreateTodoListArgs, ListTodosArgs, UpdateTodoArgs } from './todos.js';
25
27
  /**
26
28
  * The MCP tool surface is NOT exported here. It lives at the `/tools` subpath,
27
29
  * which is ESM-only, because it depends on @modelcontextprotocol/sdk — an
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC1I,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACnF,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEnE;;;;;;;;;;GAUG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC1I,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACnF,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjG;;;;;;;;;;GAUG"}
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ export { EmbeddingCache } from './embedding-cache.js';
11
11
  export { MEMORIES_DISABLED_MESSAGE, memoriesAllowed } from './memories-enabled.js';
12
12
  export { ATTRS_MAX_BYTES, TAGS_PROP, decodeAttrs, decodeSchema, defineProp, emptyAttrs, emptySchema, encodeAttrs, encodeSchema, newPropId, removeProp, reorderProps, sortedProps, tagsFromAttrs, updateProp, withTags, } from './attrs.js';
13
13
  export { buildTitleMatcher, extractLinkIds, htmlToText } from './attrs-extract.js';
14
+ export { TodoService } from './todos.js';
14
15
  /**
15
16
  * The MCP tool surface is NOT exported here. It lives at the `/tools` subpath,
16
17
  * which is ESM-only, because it depends on @modelcontextprotocol/sdk — an
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGnF;;;;;;;;;;GAUG"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC;;;;;;;;;;GAUG"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * To-do: the stored format, shared with the iOS app.
3
+ *
4
+ * A task is a note of type `todo`: its text is the encrypted title, its
5
+ * details are the JSON below as the encrypted content, and the note's
6
+ * `archived` flag means done. A list is a note of type `todolist`: its name
7
+ * is the encrypted title and `{v, color, emoji}` the encrypted content. The
8
+ * API lists neither with notes, so apps that predate them never show them.
9
+ *
10
+ * Missing keys read as their defaults (the first iOS build stored tasks with
11
+ * empty content), and keys this version does not know are kept on write, so
12
+ * a newer app's additions survive an older app's edit.
13
+ */
14
+ export declare const TODO_NOTE_TYPE = "todo";
15
+ export declare const TODO_LIST_NOTE_TYPE = "todolist";
16
+ export type Repeat = 'daily' | 'weekdays' | 'weekly' | 'monthly' | 'yearly';
17
+ export declare const REPEATS: Repeat[];
18
+ export interface Step {
19
+ id: string;
20
+ text: string;
21
+ done: boolean;
22
+ }
23
+ export interface TaskDetails {
24
+ v: 1;
25
+ /** A todolist note id; null is the built-in Tasks list. */
26
+ list: string | null;
27
+ important: boolean;
28
+ /** The local day the task was put in My Day; it is in My Day only that day. */
29
+ myDay: string | null;
30
+ /** Local day, YYYY-MM-DD. */
31
+ due: string | null;
32
+ /** ISO time of the pending reminder in /api/reminders for this task. */
33
+ remindAt: string | null;
34
+ repeat: Repeat | null;
35
+ steps: Step[];
36
+ note: string;
37
+ completedAt: string | null;
38
+ /** Keys a newer app wrote, kept as they are. */
39
+ [key: string]: unknown;
40
+ }
41
+ export interface ListMeta {
42
+ v: 1;
43
+ color: string;
44
+ emoji: string | null;
45
+ [key: string]: unknown;
46
+ }
47
+ export declare const DEFAULT_LIST_COLOR = "#5c7fb5";
48
+ export declare function parseDetails(raw: string): TaskDetails;
49
+ export declare function serializeDetails(details: TaskDetails): string;
50
+ export declare function parseListMeta(raw: string): ListMeta;
51
+ export declare function serializeListMeta(meta: ListMeta): string;
52
+ export declare function newId(): string;
53
+ /** A local day as YYYY-MM-DD. */
54
+ export declare function dayKey(date: Date): string;
55
+ /** Local midnight of a YYYY-MM-DD day. */
56
+ export declare function fromDayKey(key: string): Date;
57
+ export declare function addDays(key: string, days: number): string;
58
+ /** Whole days from a to b (b later is positive), by local calendar. */
59
+ export declare function daysBetween(a: string, b: string): number;
60
+ /**
61
+ * The next day a repeating task is due, after `from`. Month and year steps
62
+ * keep the day of the month where it exists and use the month's last day
63
+ * where it does not (Jan 31 → Feb 28).
64
+ */
65
+ export declare function nextOccurrence(repeat: Repeat, from: string): string;
66
+ export interface Task {
67
+ id: string;
68
+ title: string;
69
+ details: TaskDetails;
70
+ done: boolean;
71
+ createdAt: string;
72
+ updatedAt: string;
73
+ }
74
+ export interface TodoList {
75
+ id: string;
76
+ name: string;
77
+ meta: ListMeta;
78
+ createdAt: string;
79
+ }
80
+ export type SmartListId = 'myday' | 'important' | 'planned' | 'tasks';
81
+ /** A smart list, or a todolist note id. */
82
+ export type ListKey = SmartListId | `list:${string}`;
83
+ export declare const SMART_LISTS: {
84
+ id: SmartListId;
85
+ label: string;
86
+ }[];
87
+ export declare function listKey(listId: string): ListKey;
88
+ export declare function listIdOf(key: ListKey): string | null;
89
+ export declare function inList(task: Task, key: ListKey, today: string): boolean;
90
+ /** Open tasks oldest first, the order they were written in. */
91
+ export declare function openTasks(tasks: Task[], key: ListKey, today: string): Task[];
92
+ /** Done tasks, most recently finished first. */
93
+ export declare function completedTasks(tasks: Task[], key: ListKey, today: string): Task[];
94
+ export type PlannedGroup = 'Overdue' | 'Today' | 'Tomorrow' | 'Later';
95
+ /** Planned's open tasks in groups, each by due day then age. Empty groups are left out. */
96
+ export declare function plannedGroups(tasks: Task[], today: string): {
97
+ group: PlannedGroup;
98
+ tasks: Task[];
99
+ }[];
100
+ /** What a task added to this list starts with. */
101
+ export declare function detailsForNewTask(key: ListKey, today: string): TaskDetails;
102
+ /**
103
+ * The task that follows a repeating one when it is done: the same text, list,
104
+ * importance, repeat, note and steps (all open again), due on the next
105
+ * occurrence after its due day, or after today when it had none. A reminder
106
+ * moves by the same number of days.
107
+ */
108
+ export declare function nextRepeat(task: Task, today: string): TaskDetails | null;
109
+ export declare function repeatLabel(repeat: Repeat): string;
110
+ /** "Today", "Tomorrow", "Yesterday", "Mon, Oct 20", or with the year when not this year. */
111
+ export declare function dueLabel(due: string, today: string): string;
112
+ //# sourceMappingURL=todo-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"todo-format.d.ts","sourceRoot":"","sources":["../src/todo-format.ts"],"names":[],"mappings":"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"}