@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.
Files changed (98) hide show
  1. package/dist/noteplan/embeddings.d.ts +8 -0
  2. package/dist/noteplan/embeddings.d.ts.map +1 -1
  3. package/dist/noteplan/embeddings.js +3 -3
  4. package/dist/noteplan/embeddings.js.map +1 -1
  5. package/dist/noteplan/file-reader.d.ts +6 -0
  6. package/dist/noteplan/file-reader.d.ts.map +1 -1
  7. package/dist/noteplan/file-reader.js +15 -0
  8. package/dist/noteplan/file-reader.js.map +1 -1
  9. package/dist/noteplan/file-writer.d.ts.map +1 -1
  10. package/dist/noteplan/file-writer.js +86 -17
  11. package/dist/noteplan/file-writer.js.map +1 -1
  12. package/dist/noteplan/file-writer.test.d.ts +2 -0
  13. package/dist/noteplan/file-writer.test.d.ts.map +1 -0
  14. package/dist/noteplan/file-writer.test.js +896 -0
  15. package/dist/noteplan/file-writer.test.js.map +1 -0
  16. package/dist/noteplan/filter-store.d.ts.map +1 -1
  17. package/dist/noteplan/filter-store.js +13 -1
  18. package/dist/noteplan/filter-store.js.map +1 -1
  19. package/dist/noteplan/frontmatter-parser.d.ts +10 -1
  20. package/dist/noteplan/frontmatter-parser.d.ts.map +1 -1
  21. package/dist/noteplan/frontmatter-parser.js +59 -6
  22. package/dist/noteplan/frontmatter-parser.js.map +1 -1
  23. package/dist/noteplan/frontmatter-parser.test.js +576 -1
  24. package/dist/noteplan/frontmatter-parser.test.js.map +1 -1
  25. package/dist/noteplan/markdown-parser.d.ts +6 -1
  26. package/dist/noteplan/markdown-parser.d.ts.map +1 -1
  27. package/dist/noteplan/markdown-parser.js +25 -46
  28. package/dist/noteplan/markdown-parser.js.map +1 -1
  29. package/dist/noteplan/markdown-parser.test.d.ts +2 -0
  30. package/dist/noteplan/markdown-parser.test.d.ts.map +1 -0
  31. package/dist/noteplan/markdown-parser.test.js +690 -0
  32. package/dist/noteplan/markdown-parser.test.js.map +1 -0
  33. package/dist/noteplan/template-docs.d.ts +35 -0
  34. package/dist/noteplan/template-docs.d.ts.map +1 -0
  35. package/dist/noteplan/template-docs.js +184 -0
  36. package/dist/noteplan/template-docs.js.map +1 -0
  37. package/dist/noteplan/unified-store.d.ts +2 -0
  38. package/dist/noteplan/unified-store.d.ts.map +1 -1
  39. package/dist/noteplan/unified-store.js +22 -6
  40. package/dist/noteplan/unified-store.js.map +1 -1
  41. package/dist/server.d.ts.map +1 -1
  42. package/dist/server.js +661 -241
  43. package/dist/server.js.map +1 -1
  44. package/dist/tools/attachments.d.ts +151 -0
  45. package/dist/tools/attachments.d.ts.map +1 -0
  46. package/dist/tools/attachments.js +421 -0
  47. package/dist/tools/attachments.js.map +1 -0
  48. package/dist/tools/attachments.test.d.ts +2 -0
  49. package/dist/tools/attachments.test.d.ts.map +1 -0
  50. package/dist/tools/attachments.test.js +561 -0
  51. package/dist/tools/attachments.test.js.map +1 -0
  52. package/dist/tools/calendar.d.ts +7 -7
  53. package/dist/tools/notes.d.ts +148 -48
  54. package/dist/tools/notes.d.ts.map +1 -1
  55. package/dist/tools/notes.js +366 -29
  56. package/dist/tools/notes.js.map +1 -1
  57. package/dist/tools/notes.test.d.ts +2 -0
  58. package/dist/tools/notes.test.d.ts.map +1 -0
  59. package/dist/tools/notes.test.js +800 -0
  60. package/dist/tools/notes.test.js.map +1 -0
  61. package/dist/tools/plugins.d.ts.map +1 -1
  62. package/dist/tools/plugins.js +1 -0
  63. package/dist/tools/plugins.js.map +1 -1
  64. package/dist/tools/reminders.d.ts +4 -4
  65. package/dist/tools/search.d.ts +2 -2
  66. package/dist/tools/search.d.ts.map +1 -1
  67. package/dist/tools/search.js +32 -4
  68. package/dist/tools/search.js.map +1 -1
  69. package/dist/tools/tasks.d.ts +10 -10
  70. package/dist/tools/tasks.d.ts.map +1 -1
  71. package/dist/tools/tasks.js +14 -27
  72. package/dist/tools/tasks.js.map +1 -1
  73. package/dist/tools/templates.d.ts +130 -0
  74. package/dist/tools/templates.d.ts.map +1 -0
  75. package/dist/tools/templates.js +217 -0
  76. package/dist/tools/templates.js.map +1 -0
  77. package/dist/tools/templates.test.d.ts +2 -0
  78. package/dist/tools/templates.test.d.ts.map +1 -0
  79. package/dist/tools/templates.test.js +48 -0
  80. package/dist/tools/templates.test.js.map +1 -0
  81. package/dist/tools/ui.d.ts +2 -0
  82. package/dist/tools/ui.d.ts.map +1 -1
  83. package/dist/tools/ui.js +24 -0
  84. package/dist/tools/ui.js.map +1 -1
  85. package/dist/utils/applescript.d.ts.map +1 -1
  86. package/dist/utils/applescript.js +21 -0
  87. package/dist/utils/applescript.js.map +1 -1
  88. package/dist/utils/confirmation-tokens.test.d.ts +2 -0
  89. package/dist/utils/confirmation-tokens.test.d.ts.map +1 -0
  90. package/dist/utils/confirmation-tokens.test.js +159 -0
  91. package/dist/utils/confirmation-tokens.test.js.map +1 -0
  92. package/dist/utils/version.d.ts +3 -0
  93. package/dist/utils/version.d.ts.map +1 -1
  94. package/dist/utils/version.js +71 -23
  95. package/dist/utils/version.js.map +1 -1
  96. package/docs/templates.db.gz +0 -0
  97. package/docs/x-callback-url.md +318 -0
  98. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../../src/tools/notes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../../src/tools/notes.ts"],"names":[],"mappings":"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"}
@@ -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 filename',
388
- path: ['filename'],
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 result = store.createNote(params.title, params.content, {
674
- folder: params.folder,
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 === true) {
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 === true) {
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 === true) {
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 === true) {
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 to determine if it's local or space
994
- const target = resolveNoteTarget(params.id, params.filename, params.space);
995
- if (!target.note) {
996
- return { success: false, error: 'Note not found' };
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 = target.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 === true) {
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
- if (!params.newFilename) {
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 (use newTitle for TeamSpace 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, params.newFilename, keepExtension);
1097
+ const preview = store.previewRenameNoteFile(note.filename, effectiveNewFilename, keepExtension);
1070
1098
  const confirmationTarget = `${preview.fromFilename}=>${preview.toFilename}`;
1071
- if (params.dryRun === true) {
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, params.newFilename, keepExtension);
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
- const boundedStartLine = toBoundedInt(params.startLine, 1, 1, Math.max(1, allLines.length));
1646
- const boundedEndLine = toBoundedInt(params.endLine, boundedStartLine, boundedStartLine, Math.max(boundedStartLine, allLines.length));
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 === true) {
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
- const newContent = frontmatter.deleteLines(note.content, boundedStartLine, boundedEndLine);
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
- const lineIndex = params.line - 1; // Convert to 0-indexed
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
- const boundedStartLine = toBoundedInt(params.startLine, 1, 1, Math.max(1, originalLineCount));
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
- const startIndex = boundedStartLine - 1;
1787
- const lineCountToReplace = boundedEndLine - boundedStartLine + 1;
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 === true) {
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