@noteplanco/noteplan-mcp 1.1.6 → 1.1.8
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/dist/noteplan/embeddings.d.ts +8 -0
- package/dist/noteplan/embeddings.d.ts.map +1 -1
- package/dist/noteplan/embeddings.js +3 -3
- package/dist/noteplan/embeddings.js.map +1 -1
- package/dist/noteplan/file-reader.d.ts +6 -0
- package/dist/noteplan/file-reader.d.ts.map +1 -1
- package/dist/noteplan/file-reader.js +15 -0
- package/dist/noteplan/file-reader.js.map +1 -1
- package/dist/noteplan/file-writer.d.ts.map +1 -1
- package/dist/noteplan/file-writer.js +86 -17
- package/dist/noteplan/file-writer.js.map +1 -1
- package/dist/noteplan/file-writer.test.d.ts +2 -0
- package/dist/noteplan/file-writer.test.d.ts.map +1 -0
- package/dist/noteplan/file-writer.test.js +896 -0
- package/dist/noteplan/file-writer.test.js.map +1 -0
- package/dist/noteplan/filter-store.d.ts.map +1 -1
- package/dist/noteplan/filter-store.js +13 -1
- package/dist/noteplan/filter-store.js.map +1 -1
- package/dist/noteplan/frontmatter-parser.d.ts +10 -1
- package/dist/noteplan/frontmatter-parser.d.ts.map +1 -1
- package/dist/noteplan/frontmatter-parser.js +59 -6
- package/dist/noteplan/frontmatter-parser.js.map +1 -1
- package/dist/noteplan/frontmatter-parser.test.js +576 -1
- package/dist/noteplan/frontmatter-parser.test.js.map +1 -1
- package/dist/noteplan/markdown-parser.d.ts +6 -1
- package/dist/noteplan/markdown-parser.d.ts.map +1 -1
- package/dist/noteplan/markdown-parser.js +25 -46
- package/dist/noteplan/markdown-parser.js.map +1 -1
- package/dist/noteplan/markdown-parser.test.d.ts +2 -0
- package/dist/noteplan/markdown-parser.test.d.ts.map +1 -0
- package/dist/noteplan/markdown-parser.test.js +690 -0
- package/dist/noteplan/markdown-parser.test.js.map +1 -0
- package/dist/noteplan/template-docs.d.ts +35 -0
- package/dist/noteplan/template-docs.d.ts.map +1 -0
- package/dist/noteplan/template-docs.js +184 -0
- package/dist/noteplan/template-docs.js.map +1 -0
- package/dist/noteplan/unified-store.d.ts +2 -0
- package/dist/noteplan/unified-store.d.ts.map +1 -1
- package/dist/noteplan/unified-store.js +22 -6
- package/dist/noteplan/unified-store.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +661 -241
- package/dist/server.js.map +1 -1
- package/dist/tools/attachments.d.ts +151 -0
- package/dist/tools/attachments.d.ts.map +1 -0
- package/dist/tools/attachments.js +421 -0
- package/dist/tools/attachments.js.map +1 -0
- package/dist/tools/attachments.test.d.ts +2 -0
- package/dist/tools/attachments.test.d.ts.map +1 -0
- package/dist/tools/attachments.test.js +561 -0
- package/dist/tools/attachments.test.js.map +1 -0
- package/dist/tools/calendar.d.ts +7 -7
- package/dist/tools/notes.d.ts +148 -48
- package/dist/tools/notes.d.ts.map +1 -1
- package/dist/tools/notes.js +366 -29
- package/dist/tools/notes.js.map +1 -1
- package/dist/tools/notes.test.d.ts +2 -0
- package/dist/tools/notes.test.d.ts.map +1 -0
- package/dist/tools/notes.test.js +800 -0
- package/dist/tools/notes.test.js.map +1 -0
- package/dist/tools/plugins.d.ts.map +1 -1
- package/dist/tools/plugins.js +1 -0
- package/dist/tools/plugins.js.map +1 -1
- package/dist/tools/reminders.d.ts +4 -4
- package/dist/tools/search.d.ts +2 -2
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +32 -4
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/tasks.d.ts +10 -10
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +14 -27
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/templates.d.ts +130 -0
- package/dist/tools/templates.d.ts.map +1 -0
- package/dist/tools/templates.js +217 -0
- package/dist/tools/templates.js.map +1 -0
- package/dist/tools/templates.test.d.ts +2 -0
- package/dist/tools/templates.test.d.ts.map +1 -0
- package/dist/tools/templates.test.js +48 -0
- package/dist/tools/templates.test.js.map +1 -0
- package/dist/tools/ui.d.ts +2 -0
- package/dist/tools/ui.d.ts.map +1 -1
- package/dist/tools/ui.js +24 -0
- package/dist/tools/ui.js.map +1 -1
- package/dist/utils/applescript.d.ts.map +1 -1
- package/dist/utils/applescript.js +21 -0
- package/dist/utils/applescript.js.map +1 -1
- package/dist/utils/confirmation-tokens.test.d.ts +2 -0
- package/dist/utils/confirmation-tokens.test.d.ts.map +1 -0
- package/dist/utils/confirmation-tokens.test.js +159 -0
- package/dist/utils/confirmation-tokens.test.js.map +1 -0
- package/dist/utils/version.d.ts +3 -0
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +71 -23
- package/dist/utils/version.js.map +1 -1
- package/docs/templates.db.gz +0 -0
- package/docs/x-callback-url.md +318 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../../src/tools/notes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../../src/tools/notes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,OAAO,EAAE,QAAQ,EAAoD,MAAM,sBAAsB,CAAC;AAkPlG,KAAK,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AA6E5C,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBxB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;EAc1B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;EAiB5B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;EAQ3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;EAsB3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoB3B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuBzB,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmC/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwB5B,CAAC;AAGH,wBAAgB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,2BA4E5D;AAED,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC;;;;;;;;;;;;;;;;;;EA0CjE;AAgCD,wBAAgB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,2BAyHpE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;EAuClE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmFlE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmElE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6E9D;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4EpE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC;;;;YA9iChD,MAAM;eAAS,MAAM;kBAAY,MAAM;eAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2tChF;AAGD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;EAQ9B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BjC,CAAC;AAEH,wBAAgB,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,2BAoExE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,2BAkI9E;AAGD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAI/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuD9B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsB9B,CAAC;AAWH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY5B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAazB,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsB7B,CAAC;AAGH,wBAAgB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC;;;;;;;;EAqBpE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC;;;;;;;;EAqB1E;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC;;;;YA9pD9C,MAAM;eAAS,MAAM;kBAAY,MAAM;eAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwvDhF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC;;;;YA1vD9C,MAAM;eAAS,MAAM;kBAAY,MAAM;eAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;EAoyDhF;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC;;;;YAtyD1C,MAAM;eAAS,MAAM;kBAAY,MAAM;eAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAu4DhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;YAz4DpC,MAAM;eAAS,MAAM;kBAAY,MAAM;eAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA49DhF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC;;;;YA99D5C,MAAM;eAAS,MAAM;kBAAY,MAAM;eAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8lEhF;AA4BD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCvC,CAAC;AAEH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,2BA2J1F"}
|
package/dist/tools/notes.js
CHANGED
|
@@ -3,6 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import * as store from '../noteplan/unified-store.js';
|
|
5
5
|
import * as frontmatter from '../noteplan/frontmatter-parser.js';
|
|
6
|
+
import { ensureTemplateFrontmatter } from './templates.js';
|
|
6
7
|
import { issueConfirmationToken, validateAndConsumeConfirmationToken, } from '../utils/confirmation-tokens.js';
|
|
7
8
|
import { parseParagraphLine, buildParagraphLine } from '../noteplan/markdown-parser.js';
|
|
8
9
|
function toBoundedInt(value, defaultValue, min, max) {
|
|
@@ -34,6 +35,17 @@ function toOptionalBoolean(value) {
|
|
|
34
35
|
}
|
|
35
36
|
return undefined;
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Coerce a value to boolean — handles MCP delivering boolean params as strings.
|
|
40
|
+
* Returns true for boolean true or string "true".
|
|
41
|
+
*/
|
|
42
|
+
function isTrueBool(value) {
|
|
43
|
+
if (typeof value === 'boolean')
|
|
44
|
+
return value;
|
|
45
|
+
if (typeof value === 'string')
|
|
46
|
+
return value.trim().toLowerCase() === 'true';
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
37
49
|
function confirmationFailureMessage(toolName, reason) {
|
|
38
50
|
const refreshHint = `Call ${toolName} with dryRun=true to get a new confirmationToken.`;
|
|
39
51
|
if (reason === 'missing') {
|
|
@@ -286,6 +298,8 @@ export const createNoteSchema = z.object({
|
|
|
286
298
|
folder: z.string().optional().describe('Folder to create the note in. Supports smart matching (e.g., "projects" matches "10 - Projects")'),
|
|
287
299
|
create_new_folder: z.boolean().optional().describe('Set to true to create a new folder instead of matching existing ones'),
|
|
288
300
|
space: z.string().optional().describe('Space name or ID to create in (e.g., "My Team" or a UUID)'),
|
|
301
|
+
noteType: z.enum(['note', 'template']).optional().default('note').describe('Type of note to create. Use "template" to create in @Templates with proper frontmatter'),
|
|
302
|
+
templateTypes: z.array(z.enum(['empty-note', 'meeting-note', 'project-note', 'calendar-note'])).optional().describe('Template type tags — used when noteType="template"'),
|
|
289
303
|
});
|
|
290
304
|
export const updateNoteSchema = z.object({
|
|
291
305
|
filename: z.string().describe('Filename/path of the note to update'),
|
|
@@ -358,6 +372,8 @@ export const moveNoteSchema = z.object({
|
|
|
358
372
|
export const renameNoteFileSchema = z.object({
|
|
359
373
|
id: z.string().optional().describe('Note ID (preferred for TeamSpace notes)'),
|
|
360
374
|
filename: z.string().optional().describe('Filename/path of the note to rename'),
|
|
375
|
+
title: z.string().optional().describe('Note title to find and rename (fuzzy matched)'),
|
|
376
|
+
query: z.string().optional().describe('Fuzzy search query to find the note'),
|
|
361
377
|
space: z.string().optional().describe('Space name or ID to search in'),
|
|
362
378
|
newFilename: z
|
|
363
379
|
.string()
|
|
@@ -381,11 +397,11 @@ export const renameNoteFileSchema = z.object({
|
|
|
381
397
|
.optional()
|
|
382
398
|
.describe('Confirmation token issued by dryRun for rename execution'),
|
|
383
399
|
}).superRefine((input, ctx) => {
|
|
384
|
-
if (!input.id && !input.filename) {
|
|
400
|
+
if (!input.id && !input.filename && !input.title && !input.query) {
|
|
385
401
|
ctx.addIssue({
|
|
386
402
|
code: z.ZodIssueCode.custom,
|
|
387
|
-
message: 'Provide one note reference: id or
|
|
388
|
-
path: ['
|
|
403
|
+
message: 'Provide one note reference: id, filename, title, or query',
|
|
404
|
+
path: ['title'],
|
|
389
405
|
});
|
|
390
406
|
}
|
|
391
407
|
});
|
|
@@ -670,13 +686,19 @@ export function resolveNote(params) {
|
|
|
670
686
|
}
|
|
671
687
|
export function createNote(params) {
|
|
672
688
|
try {
|
|
673
|
-
const
|
|
674
|
-
|
|
689
|
+
const isTemplate = params.noteType === 'template';
|
|
690
|
+
const folder = isTemplate && !params.folder ? '@Templates' : params.folder;
|
|
691
|
+
const content = isTemplate
|
|
692
|
+
? ensureTemplateFrontmatter(params.title, params.content, params.templateTypes)
|
|
693
|
+
: params.content;
|
|
694
|
+
const result = store.createNote(params.title, content, {
|
|
695
|
+
folder,
|
|
675
696
|
space: params.space,
|
|
676
697
|
createNewFolder: params.create_new_folder,
|
|
677
698
|
});
|
|
678
699
|
return {
|
|
679
700
|
success: true,
|
|
701
|
+
tip: 'Use action "set_property" to add frontmatter fields (e.g. type, tags) or "remove_property" to delete them.',
|
|
680
702
|
note: {
|
|
681
703
|
title: result.note.title,
|
|
682
704
|
filename: result.note.filename,
|
|
@@ -722,7 +744,7 @@ export function updateNote(params) {
|
|
|
722
744
|
error: 'Empty content is blocked for noteplan_update_note. Use allowEmptyContent=true to override intentionally.',
|
|
723
745
|
};
|
|
724
746
|
}
|
|
725
|
-
if (params.dryRun
|
|
747
|
+
if (isTrueBool(params.dryRun)) {
|
|
726
748
|
const token = issueConfirmationToken({
|
|
727
749
|
tool: 'noteplan_update_note',
|
|
728
750
|
target: params.filename,
|
|
@@ -788,7 +810,7 @@ export function deleteNote(params) {
|
|
|
788
810
|
error: 'Note not found',
|
|
789
811
|
};
|
|
790
812
|
}
|
|
791
|
-
if (params.dryRun
|
|
813
|
+
if (isTrueBool(params.dryRun)) {
|
|
792
814
|
const token = issueConfirmationToken({
|
|
793
815
|
tool: 'noteplan_delete_note',
|
|
794
816
|
target: target.identifier,
|
|
@@ -852,7 +874,7 @@ export function moveNote(params) {
|
|
|
852
874
|
}
|
|
853
875
|
const preview = store.previewMoveNote(target.identifier, params.destinationFolder);
|
|
854
876
|
const confirmationTarget = `${preview.fromFilename}=>${preview.toFilename}::${preview.destinationParentId ?? preview.destinationFolder}`;
|
|
855
|
-
if (params.dryRun
|
|
877
|
+
if (isTrueBool(params.dryRun)) {
|
|
856
878
|
const token = issueConfirmationToken({
|
|
857
879
|
tool: 'noteplan_move_note',
|
|
858
880
|
target: confirmationTarget,
|
|
@@ -926,7 +948,7 @@ export function restoreNote(params) {
|
|
|
926
948
|
}
|
|
927
949
|
const preview = store.previewRestoreNote(target.identifier, params.destinationFolder);
|
|
928
950
|
const confirmationTarget = `${preview.fromIdentifier}=>${preview.toIdentifier}`;
|
|
929
|
-
if (params.dryRun
|
|
951
|
+
if (isTrueBool(params.dryRun)) {
|
|
930
952
|
const token = issueConfirmationToken({
|
|
931
953
|
tool: 'noteplan_restore_note',
|
|
932
954
|
target: confirmationTarget,
|
|
@@ -990,12 +1012,16 @@ export function restoreNote(params) {
|
|
|
990
1012
|
}
|
|
991
1013
|
export function renameNoteFile(params) {
|
|
992
1014
|
try {
|
|
993
|
-
// Resolve the note
|
|
994
|
-
const
|
|
995
|
-
if (!
|
|
996
|
-
return {
|
|
1015
|
+
// Resolve the note — supports id, filename, title, or query
|
|
1016
|
+
const resolved = resolveWritableNoteReference(params);
|
|
1017
|
+
if (!resolved.note) {
|
|
1018
|
+
return {
|
|
1019
|
+
success: false,
|
|
1020
|
+
error: resolved.error || 'Note not found',
|
|
1021
|
+
candidates: resolved.candidates,
|
|
1022
|
+
};
|
|
997
1023
|
}
|
|
998
|
-
const note =
|
|
1024
|
+
const note = resolved.note;
|
|
999
1025
|
// Space note: rename title
|
|
1000
1026
|
if (note.source === 'space') {
|
|
1001
1027
|
if (!params.newTitle) {
|
|
@@ -1006,7 +1032,7 @@ export function renameNoteFile(params) {
|
|
|
1006
1032
|
}
|
|
1007
1033
|
const writeId = note.id || note.filename;
|
|
1008
1034
|
const confirmationTarget = `${note.title}=>${params.newTitle}`;
|
|
1009
|
-
if (params.dryRun
|
|
1035
|
+
if (isTrueBool(params.dryRun)) {
|
|
1010
1036
|
const token = issueConfirmationToken({
|
|
1011
1037
|
tool: 'noteplan_rename_note_file',
|
|
1012
1038
|
target: confirmationTarget,
|
|
@@ -1059,16 +1085,18 @@ export function renameNoteFile(params) {
|
|
|
1059
1085
|
};
|
|
1060
1086
|
}
|
|
1061
1087
|
// Local note: rename file
|
|
1062
|
-
|
|
1088
|
+
// Accept newTitle as an alias for newFilename — "rename the note" typically means changing the title
|
|
1089
|
+
const effectiveNewFilename = params.newFilename || params.newTitle;
|
|
1090
|
+
if (!effectiveNewFilename) {
|
|
1063
1091
|
return {
|
|
1064
1092
|
success: false,
|
|
1065
|
-
error: 'newFilename is required for local notes
|
|
1093
|
+
error: 'newFilename or newTitle is required for local notes',
|
|
1066
1094
|
};
|
|
1067
1095
|
}
|
|
1068
1096
|
const keepExtension = params.keepExtension ?? true;
|
|
1069
|
-
const preview = store.previewRenameNoteFile(note.filename,
|
|
1097
|
+
const preview = store.previewRenameNoteFile(note.filename, effectiveNewFilename, keepExtension);
|
|
1070
1098
|
const confirmationTarget = `${preview.fromFilename}=>${preview.toFilename}`;
|
|
1071
|
-
if (params.dryRun
|
|
1099
|
+
if (isTrueBool(params.dryRun)) {
|
|
1072
1100
|
const token = issueConfirmationToken({
|
|
1073
1101
|
tool: 'noteplan_rename_note_file',
|
|
1074
1102
|
target: confirmationTarget,
|
|
@@ -1103,7 +1131,26 @@ export function renameNoteFile(params) {
|
|
|
1103
1131
|
error: confirmationFailureMessage('noteplan_rename_note_file', confirmation.reason),
|
|
1104
1132
|
};
|
|
1105
1133
|
}
|
|
1106
|
-
const renamed = store.renameNoteFile(note.filename,
|
|
1134
|
+
const renamed = store.renameNoteFile(note.filename, effectiveNewFilename, keepExtension);
|
|
1135
|
+
// Also update the # Title heading in the note content if it matches the old title
|
|
1136
|
+
const newTitle = params.newTitle || params.newFilename;
|
|
1137
|
+
if (newTitle && renamed.note.content) {
|
|
1138
|
+
const lines = renamed.note.content.split('\n');
|
|
1139
|
+
const titleLineIndex = lines.findIndex((l) => /^#\s+/.test(l));
|
|
1140
|
+
if (titleLineIndex !== -1) {
|
|
1141
|
+
const oldHeadingTitle = lines[titleLineIndex].replace(/^#\s+/, '');
|
|
1142
|
+
// Update heading if it matches the old note title (or old filename without extension)
|
|
1143
|
+
const oldTitle = note.title || '';
|
|
1144
|
+
const oldFilenameBase = note.filename.replace(/^.*\//, '').replace(/\.\w+$/, '');
|
|
1145
|
+
if (oldHeadingTitle === oldTitle || oldHeadingTitle === oldFilenameBase) {
|
|
1146
|
+
lines[titleLineIndex] = `# ${newTitle}`;
|
|
1147
|
+
const writeTarget = getWritableIdentifier(renamed.note);
|
|
1148
|
+
store.updateNote(writeTarget.identifier, lines.join('\n'), {
|
|
1149
|
+
source: writeTarget.source,
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1107
1154
|
return {
|
|
1108
1155
|
success: true,
|
|
1109
1156
|
message: `Note renamed to ${renamed.toFilename}`,
|
|
@@ -1319,6 +1366,7 @@ export function searchParagraphs(params) {
|
|
|
1319
1366
|
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
1320
1367
|
const result = {
|
|
1321
1368
|
success: true,
|
|
1369
|
+
tip: 'To search across ALL notes at once, use action "search_global" instead. It supports query "*" to match all tasks.',
|
|
1322
1370
|
query,
|
|
1323
1371
|
count: page.length,
|
|
1324
1372
|
totalCount: allMatches.length,
|
|
@@ -1427,6 +1475,7 @@ export const appendContentSchema = z.object({
|
|
|
1427
1475
|
query: z.string().optional().describe('Resolvable note query (fuzzy note lookup before append)'),
|
|
1428
1476
|
space: z.string().optional().describe('Space name or ID scope for title/date/query resolution'),
|
|
1429
1477
|
content: z.string().describe('Content to append'),
|
|
1478
|
+
heading: z.string().optional().describe('Heading or section marker text — when provided, appends at end of that section instead of end of note'),
|
|
1430
1479
|
indentationStyle: z
|
|
1431
1480
|
.enum(['tabs', 'preserve'])
|
|
1432
1481
|
.optional()
|
|
@@ -1555,6 +1604,41 @@ export function insertContent(params) {
|
|
|
1555
1604
|
const note = resolved.note;
|
|
1556
1605
|
const indentationStyle = normalizeIndentationStyle(params.indentationStyle);
|
|
1557
1606
|
let contentToInsert = params.content;
|
|
1607
|
+
// Auto-correct position when line number is provided but position is wrong
|
|
1608
|
+
// Catches LLMs sending { position: "start", line: 5 } instead of { position: "at-line", line: 5 }
|
|
1609
|
+
if (params.line !== undefined && params.position !== 'at-line') {
|
|
1610
|
+
params.position = 'at-line';
|
|
1611
|
+
}
|
|
1612
|
+
// Auto-detect raw task/checklist markdown when type is not explicitly set
|
|
1613
|
+
// Catches LLMs sending "- [ ] Buy groceries", "* [x] Done", "* Buy groceries", "+ Item" without proper type
|
|
1614
|
+
if (!params.type && /^[\t ]*[*+\-]\s+/.test(contentToInsert)) {
|
|
1615
|
+
// Determine type from the marker character
|
|
1616
|
+
const markerMatch = contentToInsert.match(/^[\t ]*([*+\-])\s+/);
|
|
1617
|
+
const markerChar = markerMatch?.[1];
|
|
1618
|
+
if (markerChar === '+') {
|
|
1619
|
+
params.type = 'checklist';
|
|
1620
|
+
}
|
|
1621
|
+
else if (markerChar === '*') {
|
|
1622
|
+
params.type = 'task';
|
|
1623
|
+
}
|
|
1624
|
+
else if (markerChar === '-' && /^[\t ]*-\s+\[[ x\->]\]\s+/.test(contentToInsert)) {
|
|
1625
|
+
// Dash with checkbox is clearly a task (plain "- text" could be a bullet, so only match with checkbox)
|
|
1626
|
+
params.type = 'task';
|
|
1627
|
+
}
|
|
1628
|
+
// Detect status from the checkbox marker if present
|
|
1629
|
+
if (params.type) {
|
|
1630
|
+
const statusMatch = contentToInsert.match(/\[(.)\]/);
|
|
1631
|
+
if (statusMatch) {
|
|
1632
|
+
const marker = statusMatch[1];
|
|
1633
|
+
if (marker === 'x')
|
|
1634
|
+
params.taskStatus = 'done';
|
|
1635
|
+
else if (marker === '-')
|
|
1636
|
+
params.taskStatus = 'cancelled';
|
|
1637
|
+
else if (marker === '>')
|
|
1638
|
+
params.taskStatus = 'scheduled';
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1558
1642
|
if (params.type) {
|
|
1559
1643
|
contentToInsert = contentToInsert
|
|
1560
1644
|
.split('\n')
|
|
@@ -1578,6 +1662,7 @@ export function insertContent(params) {
|
|
|
1578
1662
|
});
|
|
1579
1663
|
return {
|
|
1580
1664
|
success: true,
|
|
1665
|
+
tip: 'Use noteplan_paragraphs(action: "get") to inspect line numbers and content before making further edits.',
|
|
1581
1666
|
message: `Content inserted at ${params.position}`,
|
|
1582
1667
|
note: {
|
|
1583
1668
|
id: note.id,
|
|
@@ -1610,6 +1695,7 @@ export function appendContent(params) {
|
|
|
1610
1695
|
const normalized = normalizeContentIndentation(params.content, indentationStyle);
|
|
1611
1696
|
const newContent = frontmatter.insertContentAtPosition(note.content, normalized.content, {
|
|
1612
1697
|
position: 'end',
|
|
1698
|
+
heading: params.heading,
|
|
1613
1699
|
});
|
|
1614
1700
|
const writeTarget = getWritableIdentifier(note);
|
|
1615
1701
|
store.updateNote(writeTarget.identifier, newContent, {
|
|
@@ -1642,8 +1728,18 @@ export function deleteLines(params) {
|
|
|
1642
1728
|
}
|
|
1643
1729
|
const note = resolved.note;
|
|
1644
1730
|
const allLines = note.content.split('\n');
|
|
1645
|
-
|
|
1646
|
-
const
|
|
1731
|
+
// Line numbers are absolute (1-indexed), matching get_notes/getParagraphs
|
|
1732
|
+
const totalLineCount = allLines.length;
|
|
1733
|
+
const fmLineCount = frontmatter.getFrontmatterLineCount(note.content);
|
|
1734
|
+
const minLine = fmLineCount > 0 ? fmLineCount + 1 : 1;
|
|
1735
|
+
const boundedStartLine = toBoundedInt(params.startLine, minLine, minLine, Math.max(minLine, totalLineCount));
|
|
1736
|
+
const boundedEndLine = toBoundedInt(params.endLine, boundedStartLine, boundedStartLine, Math.max(boundedStartLine, totalLineCount));
|
|
1737
|
+
if (boundedStartLine <= fmLineCount) {
|
|
1738
|
+
return {
|
|
1739
|
+
success: false,
|
|
1740
|
+
error: `Lines 1-${fmLineCount} are frontmatter and cannot be deleted. Content starts at line ${fmLineCount + 1}.`,
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1647
1743
|
const lineCountToDelete = boundedEndLine - boundedStartLine + 1;
|
|
1648
1744
|
const previewStartIndex = boundedStartLine - 1;
|
|
1649
1745
|
const previewEndIndexExclusive = boundedEndLine;
|
|
@@ -1660,7 +1756,7 @@ export function deleteLines(params) {
|
|
|
1660
1756
|
? buildAttachmentWarningMessage(removedAttachmentReferences.length)
|
|
1661
1757
|
: undefined;
|
|
1662
1758
|
const confirmTarget = `${note.filename}:${boundedStartLine}-${boundedEndLine}`;
|
|
1663
|
-
if (params.dryRun
|
|
1759
|
+
if (isTrueBool(params.dryRun)) {
|
|
1664
1760
|
const token = issueConfirmationToken({
|
|
1665
1761
|
tool: 'noteplan_delete_lines',
|
|
1666
1762
|
target: confirmTarget,
|
|
@@ -1690,7 +1786,10 @@ export function deleteLines(params) {
|
|
|
1690
1786
|
error: confirmationFailureMessage('noteplan_delete_lines', confirmation.reason),
|
|
1691
1787
|
};
|
|
1692
1788
|
}
|
|
1693
|
-
|
|
1789
|
+
// Splice out lines using absolute indices (no frontmatter offset needed)
|
|
1790
|
+
const splicedLines = [...allLines];
|
|
1791
|
+
splicedLines.splice(boundedStartLine - 1, lineCountToDelete);
|
|
1792
|
+
const newContent = splicedLines.join('\n');
|
|
1694
1793
|
const writeIdentifier = note.source === 'space' ? (note.id || note.filename) : note.filename;
|
|
1695
1794
|
store.updateNote(writeIdentifier, newContent, { source: note.source });
|
|
1696
1795
|
return {
|
|
@@ -1724,13 +1823,21 @@ export function editLine(params) {
|
|
|
1724
1823
|
const note = resolved.note;
|
|
1725
1824
|
const lines = note.content.split('\n');
|
|
1726
1825
|
const originalLineCount = lines.length;
|
|
1727
|
-
|
|
1826
|
+
// Line numbers are absolute (1-indexed), matching get_notes/getParagraphs
|
|
1827
|
+
const fmLineCount = frontmatter.getFrontmatterLineCount(note.content);
|
|
1828
|
+
const lineIndex = Number(params.line) - 1; // Convert to 0-indexed
|
|
1728
1829
|
if (lineIndex < 0 || lineIndex >= lines.length) {
|
|
1729
1830
|
return {
|
|
1730
1831
|
success: false,
|
|
1731
1832
|
error: `Line ${params.line} does not exist (note has ${lines.length} lines)`,
|
|
1732
1833
|
};
|
|
1733
1834
|
}
|
|
1835
|
+
if (fmLineCount > 0 && lineIndex < fmLineCount) {
|
|
1836
|
+
return {
|
|
1837
|
+
success: false,
|
|
1838
|
+
error: `Line ${params.line} is inside frontmatter (lines 1-${fmLineCount}). Content starts at line ${fmLineCount + 1}.`,
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1734
1841
|
const originalLine = lines[lineIndex];
|
|
1735
1842
|
const indentationStyle = normalizeIndentationStyle(params.indentationStyle);
|
|
1736
1843
|
const normalized = normalizeContentIndentation(params.content, indentationStyle);
|
|
@@ -1781,13 +1888,31 @@ export function replaceLines(params) {
|
|
|
1781
1888
|
const note = resolved.note;
|
|
1782
1889
|
const allLines = note.content.split('\n');
|
|
1783
1890
|
const originalLineCount = allLines.length;
|
|
1784
|
-
|
|
1891
|
+
// Line numbers are absolute (1-indexed), matching get_notes/getParagraphs
|
|
1892
|
+
const fmLineCount = frontmatter.getFrontmatterLineCount(note.content);
|
|
1893
|
+
const minLine = fmLineCount > 0 ? fmLineCount + 1 : 1;
|
|
1894
|
+
const boundedStartLine = toBoundedInt(params.startLine, minLine, minLine, Math.max(minLine, originalLineCount));
|
|
1785
1895
|
const boundedEndLine = toBoundedInt(params.endLine, boundedStartLine, boundedStartLine, Math.max(boundedStartLine, originalLineCount));
|
|
1786
|
-
|
|
1787
|
-
|
|
1896
|
+
if (boundedStartLine <= fmLineCount) {
|
|
1897
|
+
return {
|
|
1898
|
+
success: false,
|
|
1899
|
+
error: `Lines 1-${fmLineCount} are frontmatter and cannot be replaced directly. Content starts at line ${fmLineCount + 1}.`,
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
let startIndex = boundedStartLine - 1;
|
|
1903
|
+
let lineCountToReplace = boundedEndLine - boundedStartLine + 1;
|
|
1788
1904
|
const replacedText = allLines.slice(startIndex, boundedEndLine).join('\n');
|
|
1789
1905
|
const indentationStyle = normalizeIndentationStyle(params.indentationStyle);
|
|
1790
1906
|
const normalized = normalizeContentIndentation(params.content, indentationStyle);
|
|
1907
|
+
// If replacement content includes frontmatter and the note already has
|
|
1908
|
+
// frontmatter, extend the splice to replace the old frontmatter too.
|
|
1909
|
+
// The agent's frontmatter is the intended update.
|
|
1910
|
+
const replacementHasFm = fmLineCount > 0
|
|
1911
|
+
&& frontmatter.parseNoteContent(normalized.content).hasFrontmatter;
|
|
1912
|
+
if (replacementHasFm) {
|
|
1913
|
+
lineCountToReplace += startIndex; // extend to also cover frontmatter lines
|
|
1914
|
+
startIndex = 0;
|
|
1915
|
+
}
|
|
1791
1916
|
if (params.allowEmptyContent !== true && normalized.content.trim().length === 0) {
|
|
1792
1917
|
return {
|
|
1793
1918
|
success: false,
|
|
@@ -1806,7 +1931,7 @@ export function replaceLines(params) {
|
|
|
1806
1931
|
warnings.push(`Line numbers shifted by ${lineDelta > 0 ? '+' : ''}${lineDelta} after this replacement. Re-read line numbers before the next mutation.`);
|
|
1807
1932
|
}
|
|
1808
1933
|
const target = `${note.filename}:${boundedStartLine}-${boundedEndLine}:${replacementLines.length}:${normalized.content.length}`;
|
|
1809
|
-
if (params.dryRun
|
|
1934
|
+
if (isTrueBool(params.dryRun)) {
|
|
1810
1935
|
const token = issueConfirmationToken({
|
|
1811
1936
|
tool: 'noteplan_replace_lines',
|
|
1812
1937
|
target,
|
|
@@ -1865,4 +1990,216 @@ export function replaceLines(params) {
|
|
|
1865
1990
|
};
|
|
1866
1991
|
}
|
|
1867
1992
|
}
|
|
1993
|
+
// ---------------------------------------------------------------------------
|
|
1994
|
+
// searchParagraphsGlobal — search ALL lines (including frontmatter) across notes
|
|
1995
|
+
// ---------------------------------------------------------------------------
|
|
1996
|
+
function normalizeType(value) {
|
|
1997
|
+
if (typeof value !== 'string')
|
|
1998
|
+
return undefined;
|
|
1999
|
+
const lower = value.trim().toLowerCase();
|
|
2000
|
+
if (lower === 'calendar' || lower === 'note' || lower === 'trash')
|
|
2001
|
+
return lower;
|
|
2002
|
+
return undefined;
|
|
2003
|
+
}
|
|
2004
|
+
function normalizeTypeList(values) {
|
|
2005
|
+
if (!Array.isArray(values))
|
|
2006
|
+
return undefined;
|
|
2007
|
+
const unique = new Set();
|
|
2008
|
+
for (const entry of values) {
|
|
2009
|
+
const normalized = normalizeType(entry);
|
|
2010
|
+
if (normalized)
|
|
2011
|
+
unique.add(normalized);
|
|
2012
|
+
}
|
|
2013
|
+
return unique.size > 0 ? Array.from(unique) : undefined;
|
|
2014
|
+
}
|
|
2015
|
+
function isPeriodicCalendarNote(note) {
|
|
2016
|
+
if (note.type !== 'calendar' || !note.date)
|
|
2017
|
+
return false;
|
|
2018
|
+
return note.date.includes('-');
|
|
2019
|
+
}
|
|
2020
|
+
export const searchParagraphsGlobalSchema = z.object({
|
|
2021
|
+
query: z.string().describe('Text to find across all notes (searches ALL lines including frontmatter)'),
|
|
2022
|
+
caseSensitive: z.boolean().optional().default(false).describe('Case-sensitive match (default: false)'),
|
|
2023
|
+
wholeWord: z.boolean().optional().default(false).describe('Require whole-word matches (default: false)'),
|
|
2024
|
+
status: z
|
|
2025
|
+
.enum(['open', 'done', 'cancelled', 'scheduled'])
|
|
2026
|
+
.optional()
|
|
2027
|
+
.describe('Filter results to only lines with this task status'),
|
|
2028
|
+
contextLines: z.number().min(0).max(5).optional().default(1).describe('Context lines before/after each match'),
|
|
2029
|
+
paragraphMaxChars: z
|
|
2030
|
+
.number()
|
|
2031
|
+
.min(50)
|
|
2032
|
+
.max(5000)
|
|
2033
|
+
.optional()
|
|
2034
|
+
.default(600)
|
|
2035
|
+
.describe('Maximum paragraph text chars per match'),
|
|
2036
|
+
folder: z.string().optional().describe('Restrict to a specific folder path'),
|
|
2037
|
+
space: z.string().optional().describe('Restrict to a specific space name or ID'),
|
|
2038
|
+
noteQuery: z.string().optional().describe('Filter notes by title/filename/folder substring'),
|
|
2039
|
+
noteTypes: z
|
|
2040
|
+
.array(z.enum(['calendar', 'note', 'trash']))
|
|
2041
|
+
.optional()
|
|
2042
|
+
.describe('Restrict scanned notes by type'),
|
|
2043
|
+
preferCalendar: z
|
|
2044
|
+
.boolean()
|
|
2045
|
+
.optional()
|
|
2046
|
+
.default(false)
|
|
2047
|
+
.describe('Prioritize calendar notes before maxNotes truncation'),
|
|
2048
|
+
periodicOnly: z
|
|
2049
|
+
.boolean()
|
|
2050
|
+
.optional()
|
|
2051
|
+
.default(false)
|
|
2052
|
+
.describe('When true, only scan periodic calendar notes (weekly/monthly/quarterly/yearly)'),
|
|
2053
|
+
maxNotes: z.number().min(1).max(2000).optional().default(500).describe('Maximum notes to scan'),
|
|
2054
|
+
limit: z.number().min(1).max(300).optional().default(30).describe('Maximum matches to return'),
|
|
2055
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
2056
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
2057
|
+
});
|
|
2058
|
+
export function searchParagraphsGlobal(params) {
|
|
2059
|
+
const query = typeof params?.query === 'string' ? params.query.trim() : '';
|
|
2060
|
+
if (!query) {
|
|
2061
|
+
return {
|
|
2062
|
+
success: false,
|
|
2063
|
+
error: 'query is required',
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
const caseSensitive = params.caseSensitive ?? false;
|
|
2067
|
+
const wholeWord = params.wholeWord ?? false;
|
|
2068
|
+
const contextLines = toBoundedInt(params.contextLines, 1, 0, 5);
|
|
2069
|
+
const paragraphMaxChars = toBoundedInt(params.paragraphMaxChars, 600, 50, 5000);
|
|
2070
|
+
const normalizedQuery = caseSensitive ? query : query.toLowerCase();
|
|
2071
|
+
const wildcardQuery = query === '*';
|
|
2072
|
+
const matcher = wholeWord
|
|
2073
|
+
? new RegExp(`\\b${escapeRegExp(query)}\\b`, caseSensitive ? '' : 'i')
|
|
2074
|
+
: null;
|
|
2075
|
+
const maxNotes = toBoundedInt(params.maxNotes, 500, 1, 2000);
|
|
2076
|
+
const noteQuery = typeof params.noteQuery === 'string' ? params.noteQuery.trim().toLowerCase() : '';
|
|
2077
|
+
const noteTypes = normalizeTypeList(params.noteTypes);
|
|
2078
|
+
const preferCalendar = params.preferCalendar === true;
|
|
2079
|
+
const periodicOnly = params.periodicOnly === true;
|
|
2080
|
+
const allNotes = store.listNotes({
|
|
2081
|
+
folder: params.folder,
|
|
2082
|
+
space: params.space,
|
|
2083
|
+
});
|
|
2084
|
+
let filteredNotes = noteQuery
|
|
2085
|
+
? allNotes.filter((note) => {
|
|
2086
|
+
const haystack = `${note.title} ${note.filename} ${note.folder || ''}`.toLowerCase();
|
|
2087
|
+
return haystack.includes(noteQuery);
|
|
2088
|
+
})
|
|
2089
|
+
: allNotes;
|
|
2090
|
+
if (noteTypes && noteTypes.length > 0) {
|
|
2091
|
+
filteredNotes = filteredNotes.filter((note) => noteTypes.includes(note.type));
|
|
2092
|
+
}
|
|
2093
|
+
if (periodicOnly) {
|
|
2094
|
+
filteredNotes = filteredNotes.filter((note) => isPeriodicCalendarNote(note));
|
|
2095
|
+
}
|
|
2096
|
+
if (preferCalendar) {
|
|
2097
|
+
filteredNotes = [...filteredNotes].sort((a, b) => {
|
|
2098
|
+
const aCalendar = a.type === 'calendar' ? 1 : 0;
|
|
2099
|
+
const bCalendar = b.type === 'calendar' ? 1 : 0;
|
|
2100
|
+
if (aCalendar !== bCalendar)
|
|
2101
|
+
return bCalendar - aCalendar;
|
|
2102
|
+
const aModified = a.modifiedAt?.getTime() ?? 0;
|
|
2103
|
+
const bModified = b.modifiedAt?.getTime() ?? 0;
|
|
2104
|
+
return bModified - aModified;
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
const scannedNotes = filteredNotes.slice(0, maxNotes);
|
|
2108
|
+
const truncatedByMaxNotes = filteredNotes.length > scannedNotes.length;
|
|
2109
|
+
const allMatches = [];
|
|
2110
|
+
for (const note of scannedNotes) {
|
|
2111
|
+
const allLines = note.content.split('\n');
|
|
2112
|
+
for (let lineIndex = 0; lineIndex < allLines.length; lineIndex++) {
|
|
2113
|
+
const lineContent = allLines[lineIndex];
|
|
2114
|
+
const haystack = caseSensitive ? lineContent : lineContent.toLowerCase();
|
|
2115
|
+
const isMatch = wildcardQuery
|
|
2116
|
+
? true
|
|
2117
|
+
: matcher
|
|
2118
|
+
? matcher.test(lineContent)
|
|
2119
|
+
: haystack.includes(normalizedQuery);
|
|
2120
|
+
if (!isMatch)
|
|
2121
|
+
continue;
|
|
2122
|
+
const paragraphBounds = findParagraphBounds(allLines, lineIndex);
|
|
2123
|
+
const paragraphRaw = allLines
|
|
2124
|
+
.slice(paragraphBounds.startIndex, paragraphBounds.endIndex + 1)
|
|
2125
|
+
.join('\n');
|
|
2126
|
+
const paragraphTruncated = paragraphRaw.length > paragraphMaxChars;
|
|
2127
|
+
const paragraph = paragraphTruncated
|
|
2128
|
+
? `${paragraphRaw.slice(0, Math.max(0, paragraphMaxChars - 3))}...`
|
|
2129
|
+
: paragraphRaw;
|
|
2130
|
+
const contextStart = Math.max(0, lineIndex - contextLines);
|
|
2131
|
+
const contextEnd = Math.min(allLines.length - 1, lineIndex + contextLines);
|
|
2132
|
+
const meta = parseParagraphLine(lineContent, lineIndex, lineIndex === 0);
|
|
2133
|
+
// Apply status filter (backward compat with searchTasksGlobal)
|
|
2134
|
+
if (params.status && meta.taskStatus !== params.status)
|
|
2135
|
+
continue;
|
|
2136
|
+
allMatches.push({
|
|
2137
|
+
note: {
|
|
2138
|
+
id: note.id,
|
|
2139
|
+
title: note.title,
|
|
2140
|
+
filename: note.filename,
|
|
2141
|
+
type: note.type,
|
|
2142
|
+
source: note.source,
|
|
2143
|
+
folder: note.folder,
|
|
2144
|
+
spaceId: note.spaceId,
|
|
2145
|
+
date: note.date,
|
|
2146
|
+
},
|
|
2147
|
+
lineIndex,
|
|
2148
|
+
line: lineIndex + 1,
|
|
2149
|
+
content: lineContent,
|
|
2150
|
+
// Backward-compat alias: `status` mirrors `taskStatus` for old consumers
|
|
2151
|
+
...(meta.taskStatus !== undefined && { status: meta.taskStatus }),
|
|
2152
|
+
type: meta.type,
|
|
2153
|
+
indentLevel: meta.indentLevel,
|
|
2154
|
+
...(meta.headingLevel !== undefined && { headingLevel: meta.headingLevel }),
|
|
2155
|
+
...(meta.taskStatus !== undefined && { taskStatus: meta.taskStatus }),
|
|
2156
|
+
...(meta.priority !== undefined && { priority: meta.priority }),
|
|
2157
|
+
...(meta.marker !== undefined && { marker: meta.marker }),
|
|
2158
|
+
...(meta.hasCheckbox !== undefined && { hasCheckbox: meta.hasCheckbox }),
|
|
2159
|
+
tags: meta.tags,
|
|
2160
|
+
mentions: meta.mentions,
|
|
2161
|
+
...(meta.scheduledDate !== undefined && { scheduledDate: meta.scheduledDate }),
|
|
2162
|
+
paragraphStartLine: paragraphBounds.startIndex + 1,
|
|
2163
|
+
paragraphEndLine: paragraphBounds.endIndex + 1,
|
|
2164
|
+
paragraph,
|
|
2165
|
+
paragraphTruncated,
|
|
2166
|
+
contextBefore: allLines.slice(contextStart, lineIndex),
|
|
2167
|
+
contextAfter: allLines.slice(lineIndex + 1, contextEnd + 1),
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
const offset = toBoundedInt(params.cursor ?? params.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
2172
|
+
const limit = toBoundedInt(params.limit, 30, 1, 300);
|
|
2173
|
+
const page = allMatches.slice(offset, offset + limit);
|
|
2174
|
+
const hasMore = offset + page.length < allMatches.length;
|
|
2175
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
2176
|
+
const result = {
|
|
2177
|
+
success: true,
|
|
2178
|
+
query,
|
|
2179
|
+
count: page.length,
|
|
2180
|
+
totalCount: allMatches.length,
|
|
2181
|
+
offset,
|
|
2182
|
+
limit,
|
|
2183
|
+
hasMore,
|
|
2184
|
+
nextCursor,
|
|
2185
|
+
scannedNoteCount: scannedNotes.length,
|
|
2186
|
+
totalNotes: filteredNotes.length,
|
|
2187
|
+
truncatedByMaxNotes,
|
|
2188
|
+
maxNotes,
|
|
2189
|
+
noteTypes,
|
|
2190
|
+
preferCalendar,
|
|
2191
|
+
periodicOnly,
|
|
2192
|
+
matches: page,
|
|
2193
|
+
};
|
|
2194
|
+
if (hasMore) {
|
|
2195
|
+
result.performanceHints = ['Continue with nextCursor to fetch the next global paragraph match page.'];
|
|
2196
|
+
}
|
|
2197
|
+
if (truncatedByMaxNotes) {
|
|
2198
|
+
result.performanceHints = [
|
|
2199
|
+
...(result.performanceHints ?? []),
|
|
2200
|
+
'Increase maxNotes or narrow folder/space/noteQuery to reduce truncation.',
|
|
2201
|
+
];
|
|
2202
|
+
}
|
|
2203
|
+
return result;
|
|
2204
|
+
}
|
|
1868
2205
|
//# sourceMappingURL=notes.js.map
|