@noteplanco/noteplan-mcp 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/noteplan/embeddings.d.ts +170 -0
- package/dist/noteplan/embeddings.d.ts.map +1 -0
- package/dist/noteplan/embeddings.js +684 -0
- package/dist/noteplan/embeddings.js.map +1 -0
- package/dist/noteplan/file-reader.d.ts +77 -0
- package/dist/noteplan/file-reader.d.ts.map +1 -0
- package/dist/noteplan/file-reader.js +488 -0
- package/dist/noteplan/file-reader.js.map +1 -0
- package/dist/noteplan/file-writer.d.ts +108 -0
- package/dist/noteplan/file-writer.d.ts.map +1 -0
- package/dist/noteplan/file-writer.js +621 -0
- package/dist/noteplan/file-writer.js.map +1 -0
- package/dist/noteplan/filter-store.d.ts +28 -0
- package/dist/noteplan/filter-store.d.ts.map +1 -0
- package/dist/noteplan/filter-store.js +180 -0
- package/dist/noteplan/filter-store.js.map +1 -0
- package/dist/noteplan/frontmatter-parser.d.ts +45 -0
- package/dist/noteplan/frontmatter-parser.d.ts.map +1 -0
- package/dist/noteplan/frontmatter-parser.js +259 -0
- package/dist/noteplan/frontmatter-parser.js.map +1 -0
- package/dist/noteplan/fuzzy-search.d.ts +7 -0
- package/dist/noteplan/fuzzy-search.d.ts.map +1 -0
- package/dist/noteplan/fuzzy-search.js +66 -0
- package/dist/noteplan/fuzzy-search.js.map +1 -0
- package/dist/noteplan/markdown-parser.d.ts +87 -0
- package/dist/noteplan/markdown-parser.d.ts.map +1 -0
- package/dist/noteplan/markdown-parser.js +519 -0
- package/dist/noteplan/markdown-parser.js.map +1 -0
- package/dist/noteplan/preferences.d.ts +44 -0
- package/dist/noteplan/preferences.d.ts.map +1 -0
- package/dist/noteplan/preferences.js +156 -0
- package/dist/noteplan/preferences.js.map +1 -0
- package/dist/noteplan/ripgrep-search.d.ts +29 -0
- package/dist/noteplan/ripgrep-search.d.ts.map +1 -0
- package/dist/noteplan/ripgrep-search.js +110 -0
- package/dist/noteplan/ripgrep-search.js.map +1 -0
- package/dist/noteplan/sqlite-reader.d.ts +77 -0
- package/dist/noteplan/sqlite-reader.d.ts.map +1 -0
- package/dist/noteplan/sqlite-reader.js +605 -0
- package/dist/noteplan/sqlite-reader.js.map +1 -0
- package/dist/noteplan/sqlite-writer.d.ts +63 -0
- package/dist/noteplan/sqlite-writer.d.ts.map +1 -0
- package/dist/noteplan/sqlite-writer.js +574 -0
- package/dist/noteplan/sqlite-writer.js.map +1 -0
- package/dist/noteplan/types.d.ts +97 -0
- package/dist/noteplan/types.d.ts.map +1 -0
- package/dist/noteplan/types.js +33 -0
- package/dist/noteplan/types.js.map +1 -0
- package/dist/noteplan/unified-store.d.ts +289 -0
- package/dist/noteplan/unified-store.d.ts.map +1 -0
- package/dist/noteplan/unified-store.js +1308 -0
- package/dist/noteplan/unified-store.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +2468 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/calendar.d.ts +311 -0
- package/dist/tools/calendar.d.ts.map +1 -0
- package/dist/tools/calendar.js +504 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/embeddings.d.ts +244 -0
- package/dist/tools/embeddings.d.ts.map +1 -0
- package/dist/tools/embeddings.js +226 -0
- package/dist/tools/embeddings.js.map +1 -0
- package/dist/tools/events.d.ts +176 -0
- package/dist/tools/events.d.ts.map +1 -0
- package/dist/tools/events.js +326 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/filters.d.ts +205 -0
- package/dist/tools/filters.d.ts.map +1 -0
- package/dist/tools/filters.js +347 -0
- package/dist/tools/filters.js.map +1 -0
- package/dist/tools/memory.d.ts +6 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +161 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/notes.d.ts +1221 -0
- package/dist/tools/notes.d.ts.map +1 -0
- package/dist/tools/notes.js +1868 -0
- package/dist/tools/notes.js.map +1 -0
- package/dist/tools/plugins.d.ts +140 -0
- package/dist/tools/plugins.d.ts.map +1 -0
- package/dist/tools/plugins.js +782 -0
- package/dist/tools/plugins.js.map +1 -0
- package/dist/tools/reminders.d.ts +207 -0
- package/dist/tools/reminders.d.ts.map +1 -0
- package/dist/tools/reminders.js +323 -0
- package/dist/tools/reminders.js.map +1 -0
- package/dist/tools/search.d.ts +58 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +373 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/spaces.d.ts +484 -0
- package/dist/tools/spaces.d.ts.map +1 -0
- package/dist/tools/spaces.js +870 -0
- package/dist/tools/spaces.js.map +1 -0
- package/dist/tools/tasks.d.ts +313 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +690 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/themes.d.ts +91 -0
- package/dist/tools/themes.d.ts.map +1 -0
- package/dist/tools/themes.js +294 -0
- package/dist/tools/themes.js.map +1 -0
- package/dist/tools/ui.d.ts +89 -0
- package/dist/tools/ui.d.ts.map +1 -0
- package/dist/tools/ui.js +137 -0
- package/dist/tools/ui.js.map +1 -0
- package/dist/utils/applescript.d.ts +5 -0
- package/dist/utils/applescript.d.ts.map +1 -0
- package/dist/utils/applescript.js +27 -0
- package/dist/utils/applescript.js.map +1 -0
- package/dist/utils/confirmation-tokens.d.ts +19 -0
- package/dist/utils/confirmation-tokens.d.ts.map +1 -0
- package/dist/utils/confirmation-tokens.js +58 -0
- package/dist/utils/confirmation-tokens.js.map +1 -0
- package/dist/utils/date-filters.d.ts +15 -0
- package/dist/utils/date-filters.d.ts.map +1 -0
- package/dist/utils/date-filters.js +129 -0
- package/dist/utils/date-filters.js.map +1 -0
- package/dist/utils/date-utils.d.ts +113 -0
- package/dist/utils/date-utils.d.ts.map +1 -0
- package/dist/utils/date-utils.js +341 -0
- package/dist/utils/date-utils.js.map +1 -0
- package/dist/utils/folder-matcher.d.ts +14 -0
- package/dist/utils/folder-matcher.d.ts.map +1 -0
- package/dist/utils/folder-matcher.js +191 -0
- package/dist/utils/folder-matcher.js.map +1 -0
- package/dist/utils/version.d.ts +10 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +88 -0
- package/dist/utils/version.js.map +1 -0
- package/docs/plugin-api/Calendar.md +448 -0
- package/docs/plugin-api/CalendarItem.md +198 -0
- package/docs/plugin-api/Clipboard.md +101 -0
- package/docs/plugin-api/CommandBar.md +251 -0
- package/docs/plugin-api/DataStore.md +700 -0
- package/docs/plugin-api/Editor.md +982 -0
- package/docs/plugin-api/HTMLView.md +337 -0
- package/docs/plugin-api/NoteObject.md +588 -0
- package/docs/plugin-api/NotePlan.md +398 -0
- package/docs/plugin-api/ParagraphObject.md +242 -0
- package/docs/plugin-api/RangeObject.md +56 -0
- package/docs/plugin-api/getting-started.md +545 -0
- package/docs/plugin-api/plugin-api-condensed.md +526 -0
- package/docs/plugin-api/plugin.json +26 -0
- package/docs/plugin-api/script.js +542 -0
- package/package.json +60 -0
- package/scripts/calendar-helper +0 -0
- package/scripts/reminders-helper +0 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
// Task operations
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import * as store from '../noteplan/unified-store.js';
|
|
4
|
+
import { parseTasks, filterTasksByStatus, updateTaskStatus, updateTaskContent, addTask, buildParagraphLine, } from '../noteplan/markdown-parser.js';
|
|
5
|
+
import { insertContentAtPosition } from '../noteplan/frontmatter-parser.js';
|
|
6
|
+
function toBoundedInt(value, defaultValue, min, max) {
|
|
7
|
+
const numeric = typeof value === 'number' ? value : Number(value);
|
|
8
|
+
if (!Number.isFinite(numeric))
|
|
9
|
+
return defaultValue;
|
|
10
|
+
return Math.min(max, Math.max(min, Math.floor(numeric)));
|
|
11
|
+
}
|
|
12
|
+
function escapeRegExp(value) {
|
|
13
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
14
|
+
}
|
|
15
|
+
function normalizeType(value) {
|
|
16
|
+
if (typeof value !== 'string')
|
|
17
|
+
return null;
|
|
18
|
+
const normalized = value.trim().toLowerCase();
|
|
19
|
+
if (normalized === 'note' || normalized === 'calendar' || normalized === 'trash') {
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function normalizeTypeList(values) {
|
|
25
|
+
if (!Array.isArray(values))
|
|
26
|
+
return undefined;
|
|
27
|
+
const unique = new Set();
|
|
28
|
+
for (const entry of values) {
|
|
29
|
+
const normalized = normalizeType(entry);
|
|
30
|
+
if (normalized)
|
|
31
|
+
unique.add(normalized);
|
|
32
|
+
}
|
|
33
|
+
return unique.size > 0 ? Array.from(unique) : undefined;
|
|
34
|
+
}
|
|
35
|
+
function isPeriodicCalendarNote(note) {
|
|
36
|
+
if (note.type !== 'calendar' || !note.date)
|
|
37
|
+
return false;
|
|
38
|
+
return note.date.includes('-');
|
|
39
|
+
}
|
|
40
|
+
function resolveTaskLineIndex(input) {
|
|
41
|
+
const hasLineIndex = typeof input.lineIndex === 'number' && Number.isFinite(input.lineIndex);
|
|
42
|
+
const hasLine = typeof input.line === 'number' && Number.isFinite(input.line);
|
|
43
|
+
if (!hasLineIndex && !hasLine) {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
error: 'Provide lineIndex (0-based) or line (1-based)',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const resolvedFromLine = hasLine ? Math.floor(input.line) - 1 : undefined;
|
|
50
|
+
const resolvedFromIndex = hasLineIndex ? Math.floor(input.lineIndex) : undefined;
|
|
51
|
+
if (resolvedFromLine !== undefined && resolvedFromLine < 0) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
error: 'line must be >= 1',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (resolvedFromIndex !== undefined && resolvedFromIndex < 0) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
error: 'lineIndex must be >= 0',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (resolvedFromLine !== undefined &&
|
|
64
|
+
resolvedFromIndex !== undefined &&
|
|
65
|
+
resolvedFromLine !== resolvedFromIndex) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
error: 'line and lineIndex reference different task lines',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
lineIndex: resolvedFromIndex ?? resolvedFromLine,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a string looks like a date target (not a filename)
|
|
78
|
+
* Matches: today, tomorrow, yesterday, YYYYMMDD, YYYY-MM-DD
|
|
79
|
+
*/
|
|
80
|
+
function isDateTarget(target) {
|
|
81
|
+
const lower = target.toLowerCase().trim();
|
|
82
|
+
// Special keywords
|
|
83
|
+
if (['today', 'tomorrow', 'yesterday'].includes(lower)) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
// YYYYMMDD format
|
|
87
|
+
if (/^\d{8}$/.test(target)) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
// YYYY-MM-DD format
|
|
91
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(target)) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
export const getTasksSchema = z.object({
|
|
97
|
+
id: z.string().optional().describe('Note ID (preferred for space notes)'),
|
|
98
|
+
title: z.string().optional().describe('Note title to search for'),
|
|
99
|
+
filename: z.string().optional().describe('Direct filename/path to the note'),
|
|
100
|
+
date: z
|
|
101
|
+
.string()
|
|
102
|
+
.optional()
|
|
103
|
+
.describe('Date for calendar notes (YYYYMMDD, YYYY-MM-DD, today, tomorrow, yesterday)'),
|
|
104
|
+
space: z.string().optional().describe('Space name or ID to search in'),
|
|
105
|
+
status: z
|
|
106
|
+
.enum(['open', 'done', 'cancelled', 'scheduled'])
|
|
107
|
+
.optional()
|
|
108
|
+
.describe('Filter by task status'),
|
|
109
|
+
query: z.string().optional().describe('Filter tasks by content substring'),
|
|
110
|
+
limit: z.number().min(1).max(500).optional().default(100).describe('Maximum tasks to return'),
|
|
111
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
112
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
113
|
+
}).superRefine((input, ctx) => {
|
|
114
|
+
if (!input.id && !input.title && !input.filename && !input.date) {
|
|
115
|
+
ctx.addIssue({
|
|
116
|
+
code: z.ZodIssueCode.custom,
|
|
117
|
+
message: 'Provide one note reference: id, title, filename, or date',
|
|
118
|
+
path: ['id'],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
export const searchTasksSchema = z.object({
|
|
123
|
+
id: z.string().optional().describe('Note ID (preferred for space notes)'),
|
|
124
|
+
title: z.string().optional().describe('Note title to search for'),
|
|
125
|
+
filename: z.string().optional().describe('Direct filename/path to the note'),
|
|
126
|
+
date: z.string().optional().describe('Date for calendar notes (YYYYMMDD, YYYY-MM-DD, today, tomorrow, yesterday)'),
|
|
127
|
+
space: z.string().optional().describe('Space name or ID to search in'),
|
|
128
|
+
query: z.string().describe('Task query text'),
|
|
129
|
+
caseSensitive: z.boolean().optional().default(false).describe('Case-sensitive task text search'),
|
|
130
|
+
wholeWord: z.boolean().optional().default(false).describe('Whole-word task text match'),
|
|
131
|
+
status: z
|
|
132
|
+
.enum(['open', 'done', 'cancelled', 'scheduled'])
|
|
133
|
+
.optional()
|
|
134
|
+
.describe('Filter by task status before query match'),
|
|
135
|
+
limit: z.number().min(1).max(200).optional().default(20).describe('Maximum matches to return'),
|
|
136
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
137
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
138
|
+
}).superRefine((input, ctx) => {
|
|
139
|
+
if (!input.id && !input.title && !input.filename && !input.date) {
|
|
140
|
+
ctx.addIssue({
|
|
141
|
+
code: z.ZodIssueCode.custom,
|
|
142
|
+
message: 'Provide one note reference: id, title, filename, or date',
|
|
143
|
+
path: ['id'],
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
export const searchTasksGlobalSchema = z.object({
|
|
148
|
+
query: z.string().describe('Task query text across notes'),
|
|
149
|
+
caseSensitive: z.boolean().optional().default(false).describe('Case-sensitive task text search'),
|
|
150
|
+
wholeWord: z.boolean().optional().default(false).describe('Whole-word task text match'),
|
|
151
|
+
status: z
|
|
152
|
+
.enum(['open', 'done', 'cancelled', 'scheduled'])
|
|
153
|
+
.optional()
|
|
154
|
+
.describe('Filter by task status before query match'),
|
|
155
|
+
folder: z.string().optional().describe('Restrict to a specific folder path'),
|
|
156
|
+
space: z.string().optional().describe('Restrict to a specific space name or ID'),
|
|
157
|
+
noteQuery: z.string().optional().describe('Filter notes by title/filename/folder substring'),
|
|
158
|
+
noteTypes: z
|
|
159
|
+
.array(z.enum(['calendar', 'note', 'trash']))
|
|
160
|
+
.optional()
|
|
161
|
+
.describe('Restrict scanned notes by type'),
|
|
162
|
+
preferCalendar: z
|
|
163
|
+
.boolean()
|
|
164
|
+
.optional()
|
|
165
|
+
.default(false)
|
|
166
|
+
.describe('Prioritize calendar notes before maxNotes truncation'),
|
|
167
|
+
periodicOnly: z
|
|
168
|
+
.boolean()
|
|
169
|
+
.optional()
|
|
170
|
+
.default(false)
|
|
171
|
+
.describe('When true, only scan periodic calendar notes (weekly/monthly/quarterly/yearly)'),
|
|
172
|
+
maxNotes: z.number().min(1).max(2000).optional().default(500).describe('Maximum notes to scan'),
|
|
173
|
+
limit: z.number().min(1).max(300).optional().default(30).describe('Maximum matches to return'),
|
|
174
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
175
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
176
|
+
});
|
|
177
|
+
export const addTaskSchema = z.object({
|
|
178
|
+
target: z
|
|
179
|
+
.string()
|
|
180
|
+
.describe('Target: a date (today, tomorrow, yesterday, YYYY-MM-DD, YYYYMMDD) for daily notes (creates note if needed), or a filename for project notes'),
|
|
181
|
+
content: z.string().describe('Task content (without the checkbox marker)'),
|
|
182
|
+
position: z
|
|
183
|
+
.enum(['start', 'end', 'after-heading', 'in-section'])
|
|
184
|
+
.optional()
|
|
185
|
+
.default('end')
|
|
186
|
+
.describe('Where to add the task'),
|
|
187
|
+
heading: z
|
|
188
|
+
.string()
|
|
189
|
+
.optional()
|
|
190
|
+
.describe('Heading or section marker text to add task under (when position is after-heading or in-section; matches both ## headings and **bold:** section markers)'),
|
|
191
|
+
space: z.string().optional().describe('Space name or ID when targeting daily notes'),
|
|
192
|
+
status: z
|
|
193
|
+
.enum(['open', 'done', 'cancelled', 'scheduled'])
|
|
194
|
+
.optional()
|
|
195
|
+
.describe('Task status (default: open)'),
|
|
196
|
+
priority: z
|
|
197
|
+
.number()
|
|
198
|
+
.min(1)
|
|
199
|
+
.max(3)
|
|
200
|
+
.optional()
|
|
201
|
+
.describe('Priority 1-3 (! / !! / !!!) appended to the task'),
|
|
202
|
+
indentLevel: z
|
|
203
|
+
.number()
|
|
204
|
+
.min(0)
|
|
205
|
+
.max(10)
|
|
206
|
+
.optional()
|
|
207
|
+
.describe('Tab indentation level (default: 0)'),
|
|
208
|
+
});
|
|
209
|
+
export const completeTaskSchema = z.object({
|
|
210
|
+
filename: z.string().describe('Filename/path of the note'),
|
|
211
|
+
lineIndex: z.number().optional().describe('Line index of the task (0-based)'),
|
|
212
|
+
line: z.number().optional().describe('Line number of the task (1-based)'),
|
|
213
|
+
space: z.string().optional().describe('Space name or ID to search in'),
|
|
214
|
+
}).superRefine((input, ctx) => {
|
|
215
|
+
if (input.lineIndex === undefined && input.line === undefined) {
|
|
216
|
+
ctx.addIssue({
|
|
217
|
+
code: z.ZodIssueCode.custom,
|
|
218
|
+
message: 'Provide lineIndex (0-based) or line (1-based)',
|
|
219
|
+
path: ['lineIndex'],
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
export const updateTaskSchema = z.object({
|
|
224
|
+
filename: z.string().describe('Filename/path of the note'),
|
|
225
|
+
lineIndex: z.number().optional().describe('Line index of the task (0-based)'),
|
|
226
|
+
line: z.number().optional().describe('Line number of the task (1-based)'),
|
|
227
|
+
space: z.string().optional().describe('Space name or ID to search in'),
|
|
228
|
+
content: z.string().optional().describe('New task content'),
|
|
229
|
+
allowEmptyContent: z
|
|
230
|
+
.boolean()
|
|
231
|
+
.optional()
|
|
232
|
+
.describe('Allow replacing task content with empty/blank text (default: false)'),
|
|
233
|
+
status: z
|
|
234
|
+
.enum(['open', 'done', 'cancelled', 'scheduled'])
|
|
235
|
+
.optional()
|
|
236
|
+
.describe('New task status'),
|
|
237
|
+
}).superRefine((input, ctx) => {
|
|
238
|
+
if (input.lineIndex === undefined && input.line === undefined) {
|
|
239
|
+
ctx.addIssue({
|
|
240
|
+
code: z.ZodIssueCode.custom,
|
|
241
|
+
message: 'Provide lineIndex (0-based) or line (1-based)',
|
|
242
|
+
path: ['lineIndex'],
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (input.content === undefined && input.status === undefined) {
|
|
246
|
+
ctx.addIssue({
|
|
247
|
+
code: z.ZodIssueCode.custom,
|
|
248
|
+
message: 'Provide at least one field to update: content or status',
|
|
249
|
+
path: ['content'],
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
export function getTasks(params) {
|
|
254
|
+
if (!params.id && !params.title && !params.filename && !params.date) {
|
|
255
|
+
return {
|
|
256
|
+
success: false,
|
|
257
|
+
error: 'Provide one note reference: id, title, filename, or date',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const note = store.getNote({
|
|
261
|
+
id: params.id,
|
|
262
|
+
title: params.title,
|
|
263
|
+
filename: params.filename,
|
|
264
|
+
date: params.date,
|
|
265
|
+
space: params.space,
|
|
266
|
+
});
|
|
267
|
+
if (!note) {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: 'Note not found',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
let tasks = parseTasks(note.content);
|
|
274
|
+
if (params.status) {
|
|
275
|
+
tasks = filterTasksByStatus(tasks, params.status);
|
|
276
|
+
}
|
|
277
|
+
const query = typeof params.query === 'string' ? params.query.trim().toLowerCase() : '';
|
|
278
|
+
if (query) {
|
|
279
|
+
tasks = tasks.filter((task) => task.content.toLowerCase().includes(query));
|
|
280
|
+
}
|
|
281
|
+
const offset = toBoundedInt(params.cursor ?? params.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
282
|
+
const limit = toBoundedInt(params.limit, 100, 1, 500);
|
|
283
|
+
const page = tasks.slice(offset, offset + limit);
|
|
284
|
+
const hasMore = offset + page.length < tasks.length;
|
|
285
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
286
|
+
const result = {
|
|
287
|
+
success: true,
|
|
288
|
+
note: {
|
|
289
|
+
id: note.id,
|
|
290
|
+
title: note.title,
|
|
291
|
+
filename: note.filename,
|
|
292
|
+
type: note.type,
|
|
293
|
+
source: note.source,
|
|
294
|
+
folder: note.folder,
|
|
295
|
+
spaceId: note.spaceId,
|
|
296
|
+
date: note.date,
|
|
297
|
+
},
|
|
298
|
+
taskCount: page.length,
|
|
299
|
+
totalCount: tasks.length,
|
|
300
|
+
offset,
|
|
301
|
+
limit,
|
|
302
|
+
hasMore,
|
|
303
|
+
nextCursor,
|
|
304
|
+
tasks: page.map((task) => ({
|
|
305
|
+
lineIndex: task.lineIndex,
|
|
306
|
+
line: task.lineIndex + 1,
|
|
307
|
+
content: task.content,
|
|
308
|
+
status: task.status,
|
|
309
|
+
tags: task.tags,
|
|
310
|
+
mentions: task.mentions,
|
|
311
|
+
scheduledDate: task.scheduledDate,
|
|
312
|
+
priority: task.priority,
|
|
313
|
+
indentLevel: task.indentLevel,
|
|
314
|
+
})),
|
|
315
|
+
};
|
|
316
|
+
if (hasMore) {
|
|
317
|
+
result.performanceHints = ['Continue with nextCursor to fetch the next task page.'];
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
export function searchTasks(params) {
|
|
322
|
+
const query = typeof params?.query === 'string' ? params.query.trim() : '';
|
|
323
|
+
if (!query) {
|
|
324
|
+
return {
|
|
325
|
+
success: false,
|
|
326
|
+
error: 'query is required',
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (!params.id && !params.title && !params.filename && !params.date) {
|
|
330
|
+
return {
|
|
331
|
+
success: false,
|
|
332
|
+
error: 'Provide one note reference: id, title, filename, or date',
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const note = store.getNote({
|
|
336
|
+
id: params.id,
|
|
337
|
+
title: params.title,
|
|
338
|
+
filename: params.filename,
|
|
339
|
+
date: params.date,
|
|
340
|
+
space: params.space,
|
|
341
|
+
});
|
|
342
|
+
if (!note) {
|
|
343
|
+
return {
|
|
344
|
+
success: false,
|
|
345
|
+
error: 'Note not found',
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
let tasks = parseTasks(note.content);
|
|
349
|
+
if (params.status) {
|
|
350
|
+
tasks = filterTasksByStatus(tasks, params.status);
|
|
351
|
+
}
|
|
352
|
+
const caseSensitive = params.caseSensitive ?? false;
|
|
353
|
+
const wholeWord = params.wholeWord ?? false;
|
|
354
|
+
const normalizedQuery = caseSensitive ? query : query.toLowerCase();
|
|
355
|
+
const matcher = wholeWord
|
|
356
|
+
? new RegExp(`\\b${escapeRegExp(query)}\\b`, caseSensitive ? '' : 'i')
|
|
357
|
+
: null;
|
|
358
|
+
const matches = tasks
|
|
359
|
+
.filter((task) => {
|
|
360
|
+
const haystack = caseSensitive ? task.content : task.content.toLowerCase();
|
|
361
|
+
return matcher ? matcher.test(task.content) : haystack.includes(normalizedQuery);
|
|
362
|
+
})
|
|
363
|
+
.map((task) => ({
|
|
364
|
+
lineIndex: task.lineIndex,
|
|
365
|
+
line: task.lineIndex + 1,
|
|
366
|
+
content: task.content,
|
|
367
|
+
status: task.status,
|
|
368
|
+
tags: task.tags,
|
|
369
|
+
mentions: task.mentions,
|
|
370
|
+
scheduledDate: task.scheduledDate,
|
|
371
|
+
priority: task.priority,
|
|
372
|
+
indentLevel: task.indentLevel,
|
|
373
|
+
}));
|
|
374
|
+
const offset = toBoundedInt(params.cursor ?? params.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
375
|
+
const limit = toBoundedInt(params.limit, 20, 1, 200);
|
|
376
|
+
const page = matches.slice(offset, offset + limit);
|
|
377
|
+
const hasMore = offset + page.length < matches.length;
|
|
378
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
379
|
+
const result = {
|
|
380
|
+
success: true,
|
|
381
|
+
query,
|
|
382
|
+
count: page.length,
|
|
383
|
+
totalCount: matches.length,
|
|
384
|
+
offset,
|
|
385
|
+
limit,
|
|
386
|
+
hasMore,
|
|
387
|
+
nextCursor,
|
|
388
|
+
note: {
|
|
389
|
+
id: note.id,
|
|
390
|
+
title: note.title,
|
|
391
|
+
filename: note.filename,
|
|
392
|
+
type: note.type,
|
|
393
|
+
source: note.source,
|
|
394
|
+
folder: note.folder,
|
|
395
|
+
spaceId: note.spaceId,
|
|
396
|
+
date: note.date,
|
|
397
|
+
},
|
|
398
|
+
matches: page,
|
|
399
|
+
};
|
|
400
|
+
if (hasMore) {
|
|
401
|
+
result.performanceHints = ['Continue with nextCursor to fetch the next task match page.'];
|
|
402
|
+
}
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
export function searchTasksGlobal(params) {
|
|
406
|
+
const query = typeof params?.query === 'string' ? params.query.trim() : '';
|
|
407
|
+
if (!query) {
|
|
408
|
+
return {
|
|
409
|
+
success: false,
|
|
410
|
+
error: 'query is required',
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const caseSensitive = params.caseSensitive ?? false;
|
|
414
|
+
const wholeWord = params.wholeWord ?? false;
|
|
415
|
+
const normalizedQuery = caseSensitive ? query : query.toLowerCase();
|
|
416
|
+
const wildcardQuery = query === '*';
|
|
417
|
+
const matcher = wholeWord
|
|
418
|
+
? new RegExp(`\\b${escapeRegExp(query)}\\b`, caseSensitive ? '' : 'i')
|
|
419
|
+
: null;
|
|
420
|
+
const maxNotes = toBoundedInt(params.maxNotes, 500, 1, 2000);
|
|
421
|
+
const noteQuery = typeof params.noteQuery === 'string' ? params.noteQuery.trim().toLowerCase() : '';
|
|
422
|
+
const noteTypes = normalizeTypeList(params.noteTypes);
|
|
423
|
+
const preferCalendar = params.preferCalendar === true;
|
|
424
|
+
const periodicOnly = params.periodicOnly === true;
|
|
425
|
+
const allNotes = store.listNotes({
|
|
426
|
+
folder: params.folder,
|
|
427
|
+
space: params.space,
|
|
428
|
+
});
|
|
429
|
+
let filteredNotes = noteQuery
|
|
430
|
+
? allNotes.filter((note) => {
|
|
431
|
+
const haystack = `${note.title} ${note.filename} ${note.folder || ''}`.toLowerCase();
|
|
432
|
+
return haystack.includes(noteQuery);
|
|
433
|
+
})
|
|
434
|
+
: allNotes;
|
|
435
|
+
if (noteTypes && noteTypes.length > 0) {
|
|
436
|
+
filteredNotes = filteredNotes.filter((note) => noteTypes.includes(note.type));
|
|
437
|
+
}
|
|
438
|
+
if (periodicOnly) {
|
|
439
|
+
filteredNotes = filteredNotes.filter((note) => isPeriodicCalendarNote(note));
|
|
440
|
+
}
|
|
441
|
+
if (preferCalendar) {
|
|
442
|
+
filteredNotes = [...filteredNotes].sort((a, b) => {
|
|
443
|
+
const aCalendar = a.type === 'calendar' ? 1 : 0;
|
|
444
|
+
const bCalendar = b.type === 'calendar' ? 1 : 0;
|
|
445
|
+
if (aCalendar !== bCalendar)
|
|
446
|
+
return bCalendar - aCalendar;
|
|
447
|
+
const aModified = a.modifiedAt?.getTime() ?? 0;
|
|
448
|
+
const bModified = b.modifiedAt?.getTime() ?? 0;
|
|
449
|
+
return bModified - aModified;
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
const scannedNotes = filteredNotes.slice(0, maxNotes);
|
|
453
|
+
const truncatedByMaxNotes = filteredNotes.length > scannedNotes.length;
|
|
454
|
+
const allMatches = [];
|
|
455
|
+
for (const note of scannedNotes) {
|
|
456
|
+
let tasks = parseTasks(note.content);
|
|
457
|
+
if (params.status) {
|
|
458
|
+
tasks = filterTasksByStatus(tasks, params.status);
|
|
459
|
+
}
|
|
460
|
+
tasks.forEach((task) => {
|
|
461
|
+
const haystack = caseSensitive ? task.content : task.content.toLowerCase();
|
|
462
|
+
const isMatch = wildcardQuery
|
|
463
|
+
? true
|
|
464
|
+
: matcher
|
|
465
|
+
? matcher.test(task.content)
|
|
466
|
+
: haystack.includes(normalizedQuery);
|
|
467
|
+
if (!isMatch)
|
|
468
|
+
return;
|
|
469
|
+
allMatches.push({
|
|
470
|
+
note: {
|
|
471
|
+
id: note.id,
|
|
472
|
+
title: note.title,
|
|
473
|
+
filename: note.filename,
|
|
474
|
+
type: note.type,
|
|
475
|
+
source: note.source,
|
|
476
|
+
folder: note.folder,
|
|
477
|
+
spaceId: note.spaceId,
|
|
478
|
+
date: note.date,
|
|
479
|
+
},
|
|
480
|
+
lineIndex: task.lineIndex,
|
|
481
|
+
line: task.lineIndex + 1,
|
|
482
|
+
content: task.content,
|
|
483
|
+
status: task.status,
|
|
484
|
+
tags: task.tags,
|
|
485
|
+
mentions: task.mentions,
|
|
486
|
+
scheduledDate: task.scheduledDate,
|
|
487
|
+
priority: task.priority,
|
|
488
|
+
indentLevel: task.indentLevel,
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
const offset = toBoundedInt(params.cursor ?? params.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
493
|
+
const limit = toBoundedInt(params.limit, 30, 1, 300);
|
|
494
|
+
const page = allMatches.slice(offset, offset + limit);
|
|
495
|
+
const hasMore = offset + page.length < allMatches.length;
|
|
496
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
497
|
+
const result = {
|
|
498
|
+
success: true,
|
|
499
|
+
query,
|
|
500
|
+
count: page.length,
|
|
501
|
+
totalCount: allMatches.length,
|
|
502
|
+
offset,
|
|
503
|
+
limit,
|
|
504
|
+
hasMore,
|
|
505
|
+
nextCursor,
|
|
506
|
+
scannedNoteCount: scannedNotes.length,
|
|
507
|
+
totalNotes: filteredNotes.length,
|
|
508
|
+
truncatedByMaxNotes,
|
|
509
|
+
maxNotes,
|
|
510
|
+
noteTypes,
|
|
511
|
+
preferCalendar,
|
|
512
|
+
periodicOnly,
|
|
513
|
+
matches: page,
|
|
514
|
+
};
|
|
515
|
+
if (hasMore) {
|
|
516
|
+
result.performanceHints = ['Continue with nextCursor to fetch the next global task match page.'];
|
|
517
|
+
}
|
|
518
|
+
if (truncatedByMaxNotes) {
|
|
519
|
+
result.performanceHints = [
|
|
520
|
+
...(result.performanceHints ?? []),
|
|
521
|
+
'Increase maxNotes or narrow folder/space/noteQuery to reduce truncation.',
|
|
522
|
+
];
|
|
523
|
+
}
|
|
524
|
+
return result;
|
|
525
|
+
}
|
|
526
|
+
export function addTaskToNote(params) {
|
|
527
|
+
try {
|
|
528
|
+
let note;
|
|
529
|
+
// Check if target is a date (daily note) or a filename (project note)
|
|
530
|
+
if (isDateTarget(params.target)) {
|
|
531
|
+
// Target is a date - get or create the daily note for that date
|
|
532
|
+
note = store.ensureCalendarNote(params.target, params.space);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
// Target is a filename - get the project note
|
|
536
|
+
note = store.getNote({ filename: params.target, space: params.space });
|
|
537
|
+
}
|
|
538
|
+
if (!note) {
|
|
539
|
+
return {
|
|
540
|
+
success: false,
|
|
541
|
+
error: 'Note not found',
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
let newContent;
|
|
545
|
+
if (params.position === 'in-section') {
|
|
546
|
+
// in-section is handled by frontmatter-parser's insertContentAtPosition
|
|
547
|
+
const taskLine = buildParagraphLine(params.content, 'task', {
|
|
548
|
+
taskStatus: params.status ?? 'open',
|
|
549
|
+
priority: params.priority,
|
|
550
|
+
indentLevel: params.indentLevel,
|
|
551
|
+
});
|
|
552
|
+
newContent = insertContentAtPosition(note.content, taskLine, {
|
|
553
|
+
position: 'in-section',
|
|
554
|
+
heading: params.heading,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
const taskOptions = (params.status !== undefined || params.priority !== undefined || params.indentLevel !== undefined)
|
|
559
|
+
? {
|
|
560
|
+
status: params.status,
|
|
561
|
+
priority: params.priority,
|
|
562
|
+
indentLevel: params.indentLevel,
|
|
563
|
+
}
|
|
564
|
+
: undefined;
|
|
565
|
+
newContent = addTask(note.content, params.content, params.position, params.heading, taskOptions);
|
|
566
|
+
}
|
|
567
|
+
const writeIdentifier = note.source === 'space' ? (note.id || note.filename) : note.filename;
|
|
568
|
+
store.updateNote(writeIdentifier, newContent, {
|
|
569
|
+
source: note.source,
|
|
570
|
+
});
|
|
571
|
+
return {
|
|
572
|
+
success: true,
|
|
573
|
+
message: `Task added to ${note.filename}`,
|
|
574
|
+
task: params.content,
|
|
575
|
+
targetDate: isDateTarget(params.target) ? params.target : undefined,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
return {
|
|
580
|
+
success: false,
|
|
581
|
+
error: error instanceof Error ? error.message : 'Failed to add task',
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
export function completeTask(params) {
|
|
586
|
+
try {
|
|
587
|
+
const resolved = resolveTaskLineIndex({
|
|
588
|
+
lineIndex: params.lineIndex,
|
|
589
|
+
line: params.line,
|
|
590
|
+
});
|
|
591
|
+
if (!resolved.ok) {
|
|
592
|
+
return {
|
|
593
|
+
success: false,
|
|
594
|
+
error: resolved.error,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
const lineIndex = resolved.lineIndex;
|
|
598
|
+
const note = store.getNote({ filename: params.filename, space: params.space });
|
|
599
|
+
if (!note) {
|
|
600
|
+
return {
|
|
601
|
+
success: false,
|
|
602
|
+
error: 'Note not found',
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
const lines = note.content.split('\n');
|
|
606
|
+
const originalLine = lines[lineIndex] || '';
|
|
607
|
+
const newContent = updateTaskStatus(note.content, lineIndex, 'done');
|
|
608
|
+
const writeIdentifier = note.source === 'space' ? (note.id || note.filename) : note.filename;
|
|
609
|
+
const updatedNote = store.updateNote(writeIdentifier, newContent, {
|
|
610
|
+
source: note.source,
|
|
611
|
+
});
|
|
612
|
+
const newLines = updatedNote.content.split('\n');
|
|
613
|
+
const updatedLine = newLines[lineIndex] || '';
|
|
614
|
+
return {
|
|
615
|
+
success: true,
|
|
616
|
+
message: `Task on lineIndex ${lineIndex} (line ${lineIndex + 1}) marked as done`,
|
|
617
|
+
filename: updatedNote.filename,
|
|
618
|
+
originalLine,
|
|
619
|
+
updatedLine,
|
|
620
|
+
lineIndex,
|
|
621
|
+
line: lineIndex + 1,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
return {
|
|
626
|
+
success: false,
|
|
627
|
+
error: error instanceof Error ? error.message : 'Failed to complete task',
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
export function updateTask(params) {
|
|
632
|
+
try {
|
|
633
|
+
const resolved = resolveTaskLineIndex({
|
|
634
|
+
lineIndex: params.lineIndex,
|
|
635
|
+
line: params.line,
|
|
636
|
+
});
|
|
637
|
+
if (!resolved.ok) {
|
|
638
|
+
return {
|
|
639
|
+
success: false,
|
|
640
|
+
error: resolved.error,
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
const lineIndex = resolved.lineIndex;
|
|
644
|
+
if (params.content === undefined && params.status === undefined) {
|
|
645
|
+
return {
|
|
646
|
+
success: false,
|
|
647
|
+
error: 'Provide at least one field to update: content or status',
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
if (params.content !== undefined &&
|
|
651
|
+
params.allowEmptyContent !== true &&
|
|
652
|
+
params.content.trim().length === 0) {
|
|
653
|
+
return {
|
|
654
|
+
success: false,
|
|
655
|
+
error: 'Empty task content is blocked for noteplan_update_task. Use noteplan_delete_lines or set allowEmptyContent=true.',
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
const note = store.getNote({ filename: params.filename, space: params.space });
|
|
659
|
+
if (!note) {
|
|
660
|
+
return {
|
|
661
|
+
success: false,
|
|
662
|
+
error: 'Note not found',
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
let newContent = note.content;
|
|
666
|
+
if (params.status) {
|
|
667
|
+
newContent = updateTaskStatus(newContent, lineIndex, params.status);
|
|
668
|
+
}
|
|
669
|
+
if (params.content !== undefined) {
|
|
670
|
+
newContent = updateTaskContent(newContent, lineIndex, params.content);
|
|
671
|
+
}
|
|
672
|
+
const writeIdentifier = note.source === 'space' ? (note.id || note.filename) : note.filename;
|
|
673
|
+
store.updateNote(writeIdentifier, newContent, {
|
|
674
|
+
source: note.source,
|
|
675
|
+
});
|
|
676
|
+
return {
|
|
677
|
+
success: true,
|
|
678
|
+
message: `Task on lineIndex ${lineIndex} (line ${lineIndex + 1}) updated`,
|
|
679
|
+
lineIndex,
|
|
680
|
+
line: lineIndex + 1,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
return {
|
|
685
|
+
success: false,
|
|
686
|
+
error: error instanceof Error ? error.message : 'Failed to update task',
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
//# sourceMappingURL=tasks.js.map
|