@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,870 @@
|
|
|
1
|
+
// Space operations
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import * as store from '../noteplan/unified-store.js';
|
|
4
|
+
import { issueConfirmationToken, validateAndConsumeConfirmationToken, } from '../utils/confirmation-tokens.js';
|
|
5
|
+
function toBoundedInt(value, defaultValue, min, max) {
|
|
6
|
+
const numeric = typeof value === 'number' ? value : Number(value);
|
|
7
|
+
if (!Number.isFinite(numeric))
|
|
8
|
+
return defaultValue;
|
|
9
|
+
return Math.min(max, Math.max(min, Math.floor(numeric)));
|
|
10
|
+
}
|
|
11
|
+
function toOptionalBoundedInt(value, min, max) {
|
|
12
|
+
if (value === undefined || value === null || value === '')
|
|
13
|
+
return undefined;
|
|
14
|
+
const numeric = typeof value === 'number' ? value : Number(value);
|
|
15
|
+
if (!Number.isFinite(numeric))
|
|
16
|
+
return undefined;
|
|
17
|
+
return Math.min(max, Math.max(min, Math.floor(numeric)));
|
|
18
|
+
}
|
|
19
|
+
function toOptionalBoolean(value) {
|
|
20
|
+
if (value === undefined || value === null || value === '')
|
|
21
|
+
return undefined;
|
|
22
|
+
if (typeof value === 'boolean')
|
|
23
|
+
return value;
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
const normalized = value.trim().toLowerCase();
|
|
26
|
+
if (normalized === 'true')
|
|
27
|
+
return true;
|
|
28
|
+
if (normalized === 'false')
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
function isDebugTimingsEnabled(value) {
|
|
34
|
+
if (typeof value === 'boolean')
|
|
35
|
+
return value;
|
|
36
|
+
if (typeof value === 'string') {
|
|
37
|
+
const normalized = value.trim().toLowerCase();
|
|
38
|
+
return normalized === 'true' || normalized === '1';
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function normalizeFolderPathInput(value) {
|
|
43
|
+
if (typeof value !== 'string')
|
|
44
|
+
return undefined;
|
|
45
|
+
let normalized = value.trim().replace(/\\/g, '/');
|
|
46
|
+
normalized = normalized.replace(/^\/+|\/+$/g, '');
|
|
47
|
+
if (!normalized || normalized === 'Notes')
|
|
48
|
+
return undefined;
|
|
49
|
+
if (normalized.startsWith('Notes/')) {
|
|
50
|
+
normalized = normalized.slice('Notes/'.length);
|
|
51
|
+
}
|
|
52
|
+
return normalized || undefined;
|
|
53
|
+
}
|
|
54
|
+
function confirmationFailureMessage(toolName, reason) {
|
|
55
|
+
const refreshHint = `Call ${toolName} with dryRun=true to get a new confirmationToken.`;
|
|
56
|
+
if (reason === 'missing') {
|
|
57
|
+
return `Confirmation token is required for ${toolName}. ${refreshHint}`;
|
|
58
|
+
}
|
|
59
|
+
if (reason === 'expired') {
|
|
60
|
+
return `Confirmation token is expired for ${toolName}. ${refreshHint}`;
|
|
61
|
+
}
|
|
62
|
+
return `Confirmation token is invalid for ${toolName}. ${refreshHint}`;
|
|
63
|
+
}
|
|
64
|
+
export const listSpacesSchema = z.object({
|
|
65
|
+
query: z.string().optional().describe('Filter spaces by name/id substring'),
|
|
66
|
+
limit: z.number().min(1).max(200).optional().default(50).describe('Maximum number of spaces to return'),
|
|
67
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
68
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
69
|
+
});
|
|
70
|
+
export const listTagsSchema = z.object({
|
|
71
|
+
space: z.string().optional().describe('Space name or ID to list tags from'),
|
|
72
|
+
query: z.string().optional().describe('Filter tags by substring'),
|
|
73
|
+
limit: z.number().min(1).max(500).optional().default(100).describe('Maximum number of tags to return'),
|
|
74
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
75
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
76
|
+
});
|
|
77
|
+
export const listFoldersSchema = z.object({
|
|
78
|
+
space: z.string().optional().describe('Space name or ID to list folders from'),
|
|
79
|
+
includeLocal: z
|
|
80
|
+
.boolean()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe('Include local filesystem folders (default: true when space is omitted)'),
|
|
83
|
+
includeSpaces: z
|
|
84
|
+
.boolean()
|
|
85
|
+
.optional()
|
|
86
|
+
.describe('Include space folders (default: true only when space is provided)'),
|
|
87
|
+
query: z.string().optional().describe('Filter folders by name/path substring'),
|
|
88
|
+
parentPath: z
|
|
89
|
+
.string()
|
|
90
|
+
.optional()
|
|
91
|
+
.describe('Optional parent folder path. Use canonical paths like "20 - Areas" (or "Notes/20 - Areas").'),
|
|
92
|
+
recursive: z
|
|
93
|
+
.boolean()
|
|
94
|
+
.optional()
|
|
95
|
+
.describe('When parentPath is set: true = include all descendants, false = only direct children (default: true)'),
|
|
96
|
+
maxDepth: z
|
|
97
|
+
.number()
|
|
98
|
+
.min(1)
|
|
99
|
+
.max(20)
|
|
100
|
+
.optional()
|
|
101
|
+
.describe('Max local folder depth (1 = top level, default: 1). If parentPath is provided and maxDepth is omitted, depth auto-expands to include that branch.'),
|
|
102
|
+
limit: z.number().min(1).max(500).optional().default(50).describe('Maximum number of folders to return'),
|
|
103
|
+
offset: z.number().min(0).optional().default(0).describe('Pagination offset'),
|
|
104
|
+
cursor: z.string().optional().describe('Cursor token from previous page (preferred over offset)'),
|
|
105
|
+
});
|
|
106
|
+
export const findFoldersSchema = z.object({
|
|
107
|
+
query: z.string().describe('Folder query, e.g. "project" or "inbox"'),
|
|
108
|
+
space: z.string().optional().describe('Restrict to a specific space name or ID'),
|
|
109
|
+
includeLocal: z
|
|
110
|
+
.boolean()
|
|
111
|
+
.optional()
|
|
112
|
+
.describe('Include local filesystem folders (default: true when space is omitted)'),
|
|
113
|
+
includeSpaces: z
|
|
114
|
+
.boolean()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe('Include space folders (default: true only when space is provided)'),
|
|
117
|
+
maxDepth: z.number().min(1).max(20).optional().describe('Max local folder depth (1 = top level, default: 2)'),
|
|
118
|
+
limit: z.number().min(1).max(100).optional().default(10).describe('Maximum matches to return'),
|
|
119
|
+
});
|
|
120
|
+
export const resolveFolderSchema = z.object({
|
|
121
|
+
query: z.string().describe('Folder text to resolve to one canonical folder path'),
|
|
122
|
+
space: z.string().optional().describe('Restrict to a specific space name or ID'),
|
|
123
|
+
includeLocal: z
|
|
124
|
+
.boolean()
|
|
125
|
+
.optional()
|
|
126
|
+
.describe('Include local filesystem folders (default: true when space is omitted)'),
|
|
127
|
+
includeSpaces: z
|
|
128
|
+
.boolean()
|
|
129
|
+
.optional()
|
|
130
|
+
.describe('Include space folders (default: true only when space is provided)'),
|
|
131
|
+
maxDepth: z.number().min(1).max(20).optional().describe('Max local folder depth (1 = top level, default: 2)'),
|
|
132
|
+
limit: z.number().min(1).max(20).optional().default(5).describe('Candidate matches to return'),
|
|
133
|
+
minScore: z.number().min(0).max(1).optional().default(0.88).describe('Minimum score for auto-resolution'),
|
|
134
|
+
ambiguityDelta: z
|
|
135
|
+
.number()
|
|
136
|
+
.min(0)
|
|
137
|
+
.max(1)
|
|
138
|
+
.optional()
|
|
139
|
+
.default(0.06)
|
|
140
|
+
.describe('If top scores are within this delta, treat as ambiguous'),
|
|
141
|
+
});
|
|
142
|
+
export const createFolderSchema = z.object({
|
|
143
|
+
path: z
|
|
144
|
+
.string()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe('Local folder path under Notes (e.g., "20 - Areas/Marketing")'),
|
|
147
|
+
space: z.string().optional().describe('Space name or ID for TeamSpace folder creation'),
|
|
148
|
+
name: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe('TeamSpace folder name (required when space is provided)'),
|
|
152
|
+
parent: z
|
|
153
|
+
.string()
|
|
154
|
+
.optional()
|
|
155
|
+
.describe('TeamSpace parent folder reference (ID/path/name or "root", default: space root)'),
|
|
156
|
+
}).superRefine((input, ctx) => {
|
|
157
|
+
if (input.space) {
|
|
158
|
+
if (!input.name || input.name.trim().length === 0) {
|
|
159
|
+
ctx.addIssue({
|
|
160
|
+
code: z.ZodIssueCode.custom,
|
|
161
|
+
message: 'name is required when space is provided',
|
|
162
|
+
path: ['name'],
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!input.path || input.path.trim().length === 0) {
|
|
168
|
+
ctx.addIssue({
|
|
169
|
+
code: z.ZodIssueCode.custom,
|
|
170
|
+
message: 'path is required for local folder creation',
|
|
171
|
+
path: ['path'],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
export const moveFolderSchema = z.object({
|
|
176
|
+
sourcePath: z
|
|
177
|
+
.string()
|
|
178
|
+
.optional()
|
|
179
|
+
.describe('Local source folder path under Notes'),
|
|
180
|
+
destinationFolder: z
|
|
181
|
+
.string()
|
|
182
|
+
.optional()
|
|
183
|
+
.describe('Local destination folder path under Notes (or "Notes" for root)'),
|
|
184
|
+
space: z.string().optional().describe('Space name or ID for TeamSpace folder move'),
|
|
185
|
+
source: z
|
|
186
|
+
.string()
|
|
187
|
+
.optional()
|
|
188
|
+
.describe('TeamSpace source folder reference (ID/path/name)'),
|
|
189
|
+
destination: z
|
|
190
|
+
.string()
|
|
191
|
+
.optional()
|
|
192
|
+
.describe('TeamSpace destination folder reference (ID/path/name or "root")'),
|
|
193
|
+
dryRun: z
|
|
194
|
+
.boolean()
|
|
195
|
+
.optional()
|
|
196
|
+
.describe('Preview move impact and get confirmationToken without mutating folders'),
|
|
197
|
+
confirmationToken: z
|
|
198
|
+
.string()
|
|
199
|
+
.optional()
|
|
200
|
+
.describe('Confirmation token issued by dryRun for move execution'),
|
|
201
|
+
}).superRefine((input, ctx) => {
|
|
202
|
+
if (input.space) {
|
|
203
|
+
if (!input.source || input.source.trim().length === 0) {
|
|
204
|
+
ctx.addIssue({
|
|
205
|
+
code: z.ZodIssueCode.custom,
|
|
206
|
+
message: 'source is required when space is provided',
|
|
207
|
+
path: ['source'],
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (!input.destination || input.destination.trim().length === 0) {
|
|
211
|
+
ctx.addIssue({
|
|
212
|
+
code: z.ZodIssueCode.custom,
|
|
213
|
+
message: 'destination is required when space is provided',
|
|
214
|
+
path: ['destination'],
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (!input.sourcePath || input.sourcePath.trim().length === 0) {
|
|
220
|
+
ctx.addIssue({
|
|
221
|
+
code: z.ZodIssueCode.custom,
|
|
222
|
+
message: 'sourcePath is required for local folder move',
|
|
223
|
+
path: ['sourcePath'],
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (!input.destinationFolder || input.destinationFolder.trim().length === 0) {
|
|
227
|
+
ctx.addIssue({
|
|
228
|
+
code: z.ZodIssueCode.custom,
|
|
229
|
+
message: 'destinationFolder is required for local folder move',
|
|
230
|
+
path: ['destinationFolder'],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
export const deleteFolderSchema = z.object({
|
|
235
|
+
path: z
|
|
236
|
+
.string()
|
|
237
|
+
.optional()
|
|
238
|
+
.describe('Local folder path under Notes to delete'),
|
|
239
|
+
space: z.string().optional().describe('Space name or ID for TeamSpace folder deletion'),
|
|
240
|
+
source: z
|
|
241
|
+
.string()
|
|
242
|
+
.optional()
|
|
243
|
+
.describe('TeamSpace source folder reference (ID/path/name)'),
|
|
244
|
+
dryRun: z
|
|
245
|
+
.boolean()
|
|
246
|
+
.optional()
|
|
247
|
+
.describe('Preview deletion impact and get confirmationToken without deleting'),
|
|
248
|
+
confirmationToken: z
|
|
249
|
+
.string()
|
|
250
|
+
.optional()
|
|
251
|
+
.describe('Confirmation token issued by dryRun for delete execution'),
|
|
252
|
+
}).superRefine((input, ctx) => {
|
|
253
|
+
if (input.space) {
|
|
254
|
+
if (!input.source || input.source.trim().length === 0) {
|
|
255
|
+
ctx.addIssue({
|
|
256
|
+
code: z.ZodIssueCode.custom,
|
|
257
|
+
message: 'source is required when space is provided',
|
|
258
|
+
path: ['source'],
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (!input.path || input.path.trim().length === 0) {
|
|
264
|
+
ctx.addIssue({
|
|
265
|
+
code: z.ZodIssueCode.custom,
|
|
266
|
+
message: 'path is required for local folder deletion',
|
|
267
|
+
path: ['path'],
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
export const renameFolderSchema = z.object({
|
|
272
|
+
sourcePath: z
|
|
273
|
+
.string()
|
|
274
|
+
.optional()
|
|
275
|
+
.describe('Local source folder path under Notes'),
|
|
276
|
+
newName: z.string().describe('New folder name'),
|
|
277
|
+
space: z.string().optional().describe('Space name or ID for TeamSpace folder rename'),
|
|
278
|
+
source: z
|
|
279
|
+
.string()
|
|
280
|
+
.optional()
|
|
281
|
+
.describe('TeamSpace source folder reference (ID/path/name)'),
|
|
282
|
+
dryRun: z
|
|
283
|
+
.boolean()
|
|
284
|
+
.optional()
|
|
285
|
+
.describe('Preview rename impact and get confirmationToken without mutating folders'),
|
|
286
|
+
confirmationToken: z
|
|
287
|
+
.string()
|
|
288
|
+
.optional()
|
|
289
|
+
.describe('Confirmation token issued by dryRun for rename execution'),
|
|
290
|
+
}).superRefine((input, ctx) => {
|
|
291
|
+
if (input.space) {
|
|
292
|
+
if (!input.source || input.source.trim().length === 0) {
|
|
293
|
+
ctx.addIssue({
|
|
294
|
+
code: z.ZodIssueCode.custom,
|
|
295
|
+
message: 'source is required when space is provided',
|
|
296
|
+
path: ['source'],
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (!input.sourcePath || input.sourcePath.trim().length === 0) {
|
|
302
|
+
ctx.addIssue({
|
|
303
|
+
code: z.ZodIssueCode.custom,
|
|
304
|
+
message: 'sourcePath is required for local folder rename',
|
|
305
|
+
path: ['sourcePath'],
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
export function listSpaces(params) {
|
|
310
|
+
const input = params ?? {};
|
|
311
|
+
const spaces = store.listSpaces();
|
|
312
|
+
const query = typeof input.query === 'string' ? input.query.trim().toLowerCase() : undefined;
|
|
313
|
+
const filtered = query
|
|
314
|
+
? spaces.filter((space) => `${space.name} ${space.id}`.toLowerCase().includes(query))
|
|
315
|
+
: spaces;
|
|
316
|
+
const offset = toBoundedInt(input.cursor ?? input.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
317
|
+
const limit = toBoundedInt(input.limit, 50, 1, 200);
|
|
318
|
+
const page = filtered.slice(offset, offset + limit);
|
|
319
|
+
const hasMore = offset + page.length < filtered.length;
|
|
320
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
321
|
+
return {
|
|
322
|
+
success: true,
|
|
323
|
+
count: page.length,
|
|
324
|
+
totalCount: filtered.length,
|
|
325
|
+
offset,
|
|
326
|
+
limit,
|
|
327
|
+
hasMore,
|
|
328
|
+
nextCursor,
|
|
329
|
+
spaces: page.map((s) => ({
|
|
330
|
+
id: s.id,
|
|
331
|
+
name: s.name,
|
|
332
|
+
noteCount: s.noteCount,
|
|
333
|
+
})),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
export function listTags(params) {
|
|
337
|
+
const input = params ?? {};
|
|
338
|
+
const tags = store.listTags(input.space);
|
|
339
|
+
const query = typeof input.query === 'string' ? input.query.trim().toLowerCase() : undefined;
|
|
340
|
+
const filtered = query ? tags.filter((tag) => tag.toLowerCase().includes(query)) : tags;
|
|
341
|
+
const offset = toBoundedInt(input.cursor ?? input.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
342
|
+
const limit = toBoundedInt(input.limit, 100, 1, 500);
|
|
343
|
+
const page = filtered.slice(offset, offset + limit);
|
|
344
|
+
const hasMore = offset + page.length < filtered.length;
|
|
345
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
346
|
+
return {
|
|
347
|
+
success: true,
|
|
348
|
+
count: page.length,
|
|
349
|
+
totalCount: filtered.length,
|
|
350
|
+
offset,
|
|
351
|
+
limit,
|
|
352
|
+
hasMore,
|
|
353
|
+
nextCursor,
|
|
354
|
+
tags: page,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
export function listFolders(params) {
|
|
358
|
+
const input = params ?? {};
|
|
359
|
+
const includeStageTimings = isDebugTimingsEnabled(input.debugTimings);
|
|
360
|
+
const stageTimings = {};
|
|
361
|
+
const parentPath = normalizeFolderPathInput(input.parentPath);
|
|
362
|
+
const recursive = toOptionalBoolean(input.recursive) ?? true;
|
|
363
|
+
const requestedMaxDepth = toOptionalBoundedInt(input.maxDepth, 1, 20);
|
|
364
|
+
const parentDepth = parentPath ? parentPath.split('/').filter(Boolean).length : 0;
|
|
365
|
+
const maxDepth = requestedMaxDepth
|
|
366
|
+
?? (parentPath
|
|
367
|
+
? (recursive ? 20 : Math.min(20, parentDepth + 1))
|
|
368
|
+
: 1);
|
|
369
|
+
const listStart = Date.now();
|
|
370
|
+
const folders = store.listFolders({
|
|
371
|
+
space: input.space,
|
|
372
|
+
includeLocal: toOptionalBoolean(input.includeLocal),
|
|
373
|
+
includeSpaces: toOptionalBoolean(input.includeSpaces),
|
|
374
|
+
query: typeof input.query === 'string' ? input.query : undefined,
|
|
375
|
+
maxDepth,
|
|
376
|
+
parentPath,
|
|
377
|
+
recursive,
|
|
378
|
+
});
|
|
379
|
+
const listFoldersMs = Date.now() - listStart;
|
|
380
|
+
if (includeStageTimings) {
|
|
381
|
+
stageTimings.listFoldersMs = listFoldersMs;
|
|
382
|
+
}
|
|
383
|
+
const paginateStart = Date.now();
|
|
384
|
+
const offset = toBoundedInt(input.cursor ?? input.offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
385
|
+
const limit = toBoundedInt(input.limit, 50, 1, 500);
|
|
386
|
+
const page = folders.slice(offset, offset + limit);
|
|
387
|
+
const hasMore = offset + page.length < folders.length;
|
|
388
|
+
const nextCursor = hasMore ? String(offset + page.length) : null;
|
|
389
|
+
const paginateMs = Date.now() - paginateStart;
|
|
390
|
+
if (includeStageTimings) {
|
|
391
|
+
stageTimings.paginateMs = paginateMs;
|
|
392
|
+
}
|
|
393
|
+
const mapStart = Date.now();
|
|
394
|
+
const mappedFolders = page.map((f) => ({
|
|
395
|
+
id: f.id,
|
|
396
|
+
path: f.path,
|
|
397
|
+
name: f.name,
|
|
398
|
+
source: f.source,
|
|
399
|
+
spaceId: f.spaceId,
|
|
400
|
+
}));
|
|
401
|
+
const mapResultMs = Date.now() - mapStart;
|
|
402
|
+
if (includeStageTimings) {
|
|
403
|
+
stageTimings.mapResultMs = mapResultMs;
|
|
404
|
+
}
|
|
405
|
+
const result = {
|
|
406
|
+
success: true,
|
|
407
|
+
count: page.length,
|
|
408
|
+
totalCount: folders.length,
|
|
409
|
+
offset,
|
|
410
|
+
limit,
|
|
411
|
+
maxDepth,
|
|
412
|
+
parentPath: parentPath ?? null,
|
|
413
|
+
recursive,
|
|
414
|
+
hasMore,
|
|
415
|
+
nextCursor,
|
|
416
|
+
folders: mappedFolders,
|
|
417
|
+
};
|
|
418
|
+
const performanceHints = [];
|
|
419
|
+
if (listFoldersMs > 1200) {
|
|
420
|
+
if (!input.query) {
|
|
421
|
+
performanceHints.push('Set query to narrow folder results before listing full trees.');
|
|
422
|
+
}
|
|
423
|
+
if (!input.space) {
|
|
424
|
+
performanceHints.push('Set space to scope folder listing to one workspace.');
|
|
425
|
+
}
|
|
426
|
+
if (maxDepth > 1 && requestedMaxDepth !== undefined) {
|
|
427
|
+
performanceHints.push('Lower maxDepth (for example 1) to reduce local folder traversal.');
|
|
428
|
+
}
|
|
429
|
+
if (!parentPath) {
|
|
430
|
+
performanceHints.push('Set parentPath to scope folder traversal to one branch.');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (hasMore && limit > 100) {
|
|
434
|
+
performanceHints.push('Use a smaller limit (for example 25-50) and paginate with nextCursor.');
|
|
435
|
+
}
|
|
436
|
+
if (parentPath && recursive) {
|
|
437
|
+
performanceHints.push('Set recursive=false to return only direct subfolders of parentPath.');
|
|
438
|
+
}
|
|
439
|
+
if (parentPath && requestedMaxDepth !== undefined && requestedMaxDepth <= parentDepth) {
|
|
440
|
+
performanceHints.push(`maxDepth=${requestedMaxDepth} may be too shallow for parentPath depth ${parentDepth}; set maxDepth>=${parentDepth + 1} to include children.`);
|
|
441
|
+
}
|
|
442
|
+
if (performanceHints.length > 0) {
|
|
443
|
+
result.performanceHints = performanceHints;
|
|
444
|
+
}
|
|
445
|
+
if (includeStageTimings) {
|
|
446
|
+
result.stageTimings = stageTimings;
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
function folderMatchScore(path, name, query) {
|
|
451
|
+
const pathLower = path.toLowerCase();
|
|
452
|
+
const nameLower = name.toLowerCase();
|
|
453
|
+
const queryLower = query.toLowerCase();
|
|
454
|
+
if (nameLower === queryLower)
|
|
455
|
+
return 1.0;
|
|
456
|
+
if (pathLower === queryLower)
|
|
457
|
+
return 0.98;
|
|
458
|
+
if (nameLower.startsWith(queryLower))
|
|
459
|
+
return 0.92;
|
|
460
|
+
if (pathLower.includes(`/${queryLower}`) || pathLower.startsWith(queryLower))
|
|
461
|
+
return 0.88;
|
|
462
|
+
if (nameLower.includes(queryLower))
|
|
463
|
+
return 0.82;
|
|
464
|
+
if (pathLower.includes(queryLower))
|
|
465
|
+
return 0.75;
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
export function findFolders(params) {
|
|
469
|
+
const query = typeof params?.query === 'string' ? params.query.trim() : '';
|
|
470
|
+
if (!query) {
|
|
471
|
+
return {
|
|
472
|
+
success: false,
|
|
473
|
+
error: 'query is required',
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
const limit = toBoundedInt(params.limit, 10, 1, 100);
|
|
477
|
+
const maxDepth = toOptionalBoundedInt(params.maxDepth, 1, 20) ?? 2;
|
|
478
|
+
const folders = store.listFolders({
|
|
479
|
+
space: params.space,
|
|
480
|
+
includeLocal: toOptionalBoolean(params.includeLocal),
|
|
481
|
+
includeSpaces: toOptionalBoolean(params.includeSpaces),
|
|
482
|
+
query,
|
|
483
|
+
maxDepth,
|
|
484
|
+
});
|
|
485
|
+
const scored = folders
|
|
486
|
+
.map((folder) => ({
|
|
487
|
+
folder,
|
|
488
|
+
score: folderMatchScore(folder.path, folder.name, query),
|
|
489
|
+
depth: folder.path.split('/').length,
|
|
490
|
+
}))
|
|
491
|
+
.filter((entry) => entry.score > 0)
|
|
492
|
+
.sort((a, b) => {
|
|
493
|
+
if (Math.abs(a.score - b.score) > 0.001)
|
|
494
|
+
return b.score - a.score;
|
|
495
|
+
return a.depth - b.depth;
|
|
496
|
+
})
|
|
497
|
+
.slice(0, limit);
|
|
498
|
+
return {
|
|
499
|
+
success: true,
|
|
500
|
+
query,
|
|
501
|
+
maxDepth,
|
|
502
|
+
count: scored.length,
|
|
503
|
+
matches: scored.map((entry) => ({
|
|
504
|
+
id: entry.folder.id,
|
|
505
|
+
path: entry.folder.path,
|
|
506
|
+
name: entry.folder.name,
|
|
507
|
+
source: entry.folder.source,
|
|
508
|
+
spaceId: entry.folder.spaceId,
|
|
509
|
+
score: Number(entry.score.toFixed(3)),
|
|
510
|
+
})),
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
export function resolveFolder(params) {
|
|
514
|
+
const query = typeof params?.query === 'string' ? params.query.trim() : '';
|
|
515
|
+
if (!query) {
|
|
516
|
+
return {
|
|
517
|
+
success: false,
|
|
518
|
+
error: 'query is required',
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
const limit = toBoundedInt(params.limit, 5, 1, 20);
|
|
522
|
+
const maxDepth = toOptionalBoundedInt(params.maxDepth, 1, 20) ?? 2;
|
|
523
|
+
const minScore = Math.min(1, Math.max(0, Number(params.minScore ?? 0.88)));
|
|
524
|
+
const ambiguityDelta = Math.min(1, Math.max(0, Number(params.ambiguityDelta ?? 0.06)));
|
|
525
|
+
const includeStageTimings = isDebugTimingsEnabled(params.debugTimings);
|
|
526
|
+
const stageTimings = {};
|
|
527
|
+
const listStart = Date.now();
|
|
528
|
+
const folders = store.listFolders({
|
|
529
|
+
space: params.space,
|
|
530
|
+
includeLocal: toOptionalBoolean(params.includeLocal),
|
|
531
|
+
includeSpaces: toOptionalBoolean(params.includeSpaces),
|
|
532
|
+
query,
|
|
533
|
+
maxDepth,
|
|
534
|
+
});
|
|
535
|
+
const listFoldersMs = Date.now() - listStart;
|
|
536
|
+
if (includeStageTimings) {
|
|
537
|
+
stageTimings.listFoldersMs = listFoldersMs;
|
|
538
|
+
}
|
|
539
|
+
const scoreStart = Date.now();
|
|
540
|
+
const scored = folders
|
|
541
|
+
.map((folder) => ({
|
|
542
|
+
folder,
|
|
543
|
+
score: folderMatchScore(folder.path, folder.name, query),
|
|
544
|
+
depth: folder.path.split('/').length,
|
|
545
|
+
}))
|
|
546
|
+
.filter((entry) => entry.score > 0)
|
|
547
|
+
.sort((a, b) => {
|
|
548
|
+
if (Math.abs(a.score - b.score) > 0.001)
|
|
549
|
+
return b.score - a.score;
|
|
550
|
+
return a.depth - b.depth;
|
|
551
|
+
});
|
|
552
|
+
const scoreAndSortMs = Date.now() - scoreStart;
|
|
553
|
+
if (includeStageTimings) {
|
|
554
|
+
stageTimings.scoreAndSortMs = scoreAndSortMs;
|
|
555
|
+
}
|
|
556
|
+
const resolveStart = Date.now();
|
|
557
|
+
const candidates = scored.slice(0, limit);
|
|
558
|
+
const top = candidates[0];
|
|
559
|
+
const second = candidates[1];
|
|
560
|
+
const queryLower = query.toLowerCase();
|
|
561
|
+
const exactMatch = top
|
|
562
|
+
? top.folder.name.toLowerCase() === queryLower || top.folder.path.toLowerCase() === queryLower
|
|
563
|
+
: false;
|
|
564
|
+
const scoreDelta = top && second ? top.score - second.score : 1;
|
|
565
|
+
const confident = Boolean(top) && (exactMatch || top.score >= minScore);
|
|
566
|
+
const ambiguous = Boolean(second) && scoreDelta < ambiguityDelta;
|
|
567
|
+
const resolved = confident && !ambiguous ? top : undefined;
|
|
568
|
+
const mappedCandidates = candidates.map((entry) => ({
|
|
569
|
+
id: entry.folder.id,
|
|
570
|
+
path: entry.folder.path,
|
|
571
|
+
name: entry.folder.name,
|
|
572
|
+
source: entry.folder.source,
|
|
573
|
+
spaceId: entry.folder.spaceId,
|
|
574
|
+
score: Number(entry.score.toFixed(3)),
|
|
575
|
+
}));
|
|
576
|
+
const resolveResultMs = Date.now() - resolveStart;
|
|
577
|
+
if (includeStageTimings) {
|
|
578
|
+
stageTimings.resolveResultMs = resolveResultMs;
|
|
579
|
+
}
|
|
580
|
+
const result = {
|
|
581
|
+
success: true,
|
|
582
|
+
query,
|
|
583
|
+
maxDepth,
|
|
584
|
+
count: candidates.length,
|
|
585
|
+
resolved: resolved
|
|
586
|
+
? {
|
|
587
|
+
path: resolved.folder.path,
|
|
588
|
+
id: resolved.folder.id,
|
|
589
|
+
name: resolved.folder.name,
|
|
590
|
+
source: resolved.folder.source,
|
|
591
|
+
spaceId: resolved.folder.spaceId,
|
|
592
|
+
score: Number(resolved.score.toFixed(3)),
|
|
593
|
+
}
|
|
594
|
+
: null,
|
|
595
|
+
exactMatch,
|
|
596
|
+
ambiguous,
|
|
597
|
+
confidence: top ? Number(top.score.toFixed(3)) : 0,
|
|
598
|
+
confidenceDelta: Number(scoreDelta.toFixed(3)),
|
|
599
|
+
suggestedToolArgs: resolved ? { folder: resolved.folder.path } : null,
|
|
600
|
+
candidates: mappedCandidates,
|
|
601
|
+
};
|
|
602
|
+
const performanceHints = [];
|
|
603
|
+
if (listFoldersMs > 1200) {
|
|
604
|
+
if (!params.space) {
|
|
605
|
+
performanceHints.push('Set space to narrow folder resolution to one workspace.');
|
|
606
|
+
}
|
|
607
|
+
if (params.includeLocal === false && params.includeSpaces === false) {
|
|
608
|
+
performanceHints.push('Enable includeLocal or includeSpaces to avoid empty scans.');
|
|
609
|
+
}
|
|
610
|
+
if (maxDepth > 1) {
|
|
611
|
+
performanceHints.push('Lower maxDepth (for example 1) for faster resolution.');
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (candidates.length === 0) {
|
|
615
|
+
performanceHints.push('Try noteplan_find_folders first to inspect likely folder matches.');
|
|
616
|
+
}
|
|
617
|
+
if (performanceHints.length > 0) {
|
|
618
|
+
result.performanceHints = performanceHints;
|
|
619
|
+
}
|
|
620
|
+
if (includeStageTimings) {
|
|
621
|
+
result.stageTimings = stageTimings;
|
|
622
|
+
}
|
|
623
|
+
return result;
|
|
624
|
+
}
|
|
625
|
+
export function createFolder(params) {
|
|
626
|
+
const input = params ?? {};
|
|
627
|
+
try {
|
|
628
|
+
if (input.space) {
|
|
629
|
+
const created = store.createFolder({
|
|
630
|
+
space: input.space,
|
|
631
|
+
name: input.name || '',
|
|
632
|
+
parent: input.parent,
|
|
633
|
+
});
|
|
634
|
+
if (created.source !== 'space') {
|
|
635
|
+
throw new Error('Invalid TeamSpace folder creation state');
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
success: true,
|
|
639
|
+
message: `TeamSpace folder created at ${created.path}`,
|
|
640
|
+
source: created.source,
|
|
641
|
+
spaceId: created.spaceId,
|
|
642
|
+
id: created.id,
|
|
643
|
+
path: created.path,
|
|
644
|
+
name: created.name,
|
|
645
|
+
parentId: created.parentId,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const created = store.createFolder({
|
|
649
|
+
path: input.path || '',
|
|
650
|
+
});
|
|
651
|
+
return {
|
|
652
|
+
success: true,
|
|
653
|
+
message: `Folder created at ${created.path}`,
|
|
654
|
+
source: created.source,
|
|
655
|
+
path: created.path,
|
|
656
|
+
name: created.name,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
return {
|
|
661
|
+
success: false,
|
|
662
|
+
error: error instanceof Error ? error.message : 'Failed to create folder',
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
export function moveFolder(params) {
|
|
667
|
+
const input = params ?? {};
|
|
668
|
+
try {
|
|
669
|
+
const preview = input.space
|
|
670
|
+
? store.previewMoveFolder({
|
|
671
|
+
space: input.space,
|
|
672
|
+
source: input.source || '',
|
|
673
|
+
destination: input.destination || '',
|
|
674
|
+
})
|
|
675
|
+
: store.previewMoveFolder({
|
|
676
|
+
sourcePath: input.sourcePath || '',
|
|
677
|
+
destinationFolder: input.destinationFolder || '',
|
|
678
|
+
});
|
|
679
|
+
const confirmationTarget = `${preview.fromPath}=>${preview.toPath}`;
|
|
680
|
+
if (input.dryRun === true) {
|
|
681
|
+
const token = issueConfirmationToken({
|
|
682
|
+
tool: 'noteplan_move_folder',
|
|
683
|
+
target: confirmationTarget,
|
|
684
|
+
action: 'move_folder',
|
|
685
|
+
});
|
|
686
|
+
const noteCount = preview.affectedNoteCount ?? 0;
|
|
687
|
+
const folderCount = preview.affectedFolderCount ?? 0;
|
|
688
|
+
const parts = [];
|
|
689
|
+
if (noteCount > 0)
|
|
690
|
+
parts.push(`${noteCount} note${noteCount !== 1 ? 's' : ''}`);
|
|
691
|
+
if (folderCount > 0)
|
|
692
|
+
parts.push(`${folderCount} subfolder${folderCount !== 1 ? 's' : ''}`);
|
|
693
|
+
const affectedSummary = parts.length > 0 ? ` (contains ${parts.join(' and ')})` : ' (empty)';
|
|
694
|
+
return {
|
|
695
|
+
success: true,
|
|
696
|
+
dryRun: true,
|
|
697
|
+
message: `Dry run: folder ${preview.fromPath} would move to ${preview.toPath}${affectedSummary}`,
|
|
698
|
+
...preview,
|
|
699
|
+
...token,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
const confirmation = validateAndConsumeConfirmationToken(input.confirmationToken, {
|
|
703
|
+
tool: 'noteplan_move_folder',
|
|
704
|
+
target: confirmationTarget,
|
|
705
|
+
action: 'move_folder',
|
|
706
|
+
});
|
|
707
|
+
if (!confirmation.ok) {
|
|
708
|
+
return {
|
|
709
|
+
success: false,
|
|
710
|
+
error: confirmationFailureMessage('noteplan_move_folder', confirmation.reason),
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
const moved = input.space
|
|
714
|
+
? store.moveFolder({
|
|
715
|
+
space: input.space,
|
|
716
|
+
source: input.source || '',
|
|
717
|
+
destination: input.destination || '',
|
|
718
|
+
})
|
|
719
|
+
: store.moveFolder({
|
|
720
|
+
sourcePath: input.sourcePath || '',
|
|
721
|
+
destinationFolder: input.destinationFolder || '',
|
|
722
|
+
});
|
|
723
|
+
return {
|
|
724
|
+
success: true,
|
|
725
|
+
message: moved.source === 'space'
|
|
726
|
+
? `TeamSpace folder moved to ${moved.toPath}`
|
|
727
|
+
: `Folder moved to ${moved.toPath}`,
|
|
728
|
+
...moved,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
return {
|
|
733
|
+
success: false,
|
|
734
|
+
error: error instanceof Error ? error.message : 'Failed to move folder',
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
export function deleteFolder(params) {
|
|
739
|
+
const input = params ?? {};
|
|
740
|
+
try {
|
|
741
|
+
const preview = input.space
|
|
742
|
+
? store.previewDeleteFolder({
|
|
743
|
+
space: input.space,
|
|
744
|
+
source: input.source || '',
|
|
745
|
+
})
|
|
746
|
+
: store.previewDeleteFolder({
|
|
747
|
+
path: input.path || '',
|
|
748
|
+
});
|
|
749
|
+
const confirmationTarget = preview.fromPath;
|
|
750
|
+
if (input.dryRun === true) {
|
|
751
|
+
const token = issueConfirmationToken({
|
|
752
|
+
tool: 'noteplan_delete_folder',
|
|
753
|
+
target: confirmationTarget,
|
|
754
|
+
action: 'delete_folder',
|
|
755
|
+
});
|
|
756
|
+
const noteCount = preview.affectedNoteCount ?? 0;
|
|
757
|
+
const folderCount = preview.affectedFolderCount ?? 0;
|
|
758
|
+
const parts = [];
|
|
759
|
+
if (noteCount > 0)
|
|
760
|
+
parts.push(`${noteCount} note${noteCount !== 1 ? 's' : ''}`);
|
|
761
|
+
if (folderCount > 0)
|
|
762
|
+
parts.push(`${folderCount} subfolder${folderCount !== 1 ? 's' : ''}`);
|
|
763
|
+
const affectedSummary = parts.length > 0 ? ` (contains ${parts.join(' and ')})` : ' (empty)';
|
|
764
|
+
return {
|
|
765
|
+
success: true,
|
|
766
|
+
dryRun: true,
|
|
767
|
+
message: `Dry run: folder ${preview.fromPath} would be moved to @Trash${affectedSummary}`,
|
|
768
|
+
...preview,
|
|
769
|
+
...token,
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
const confirmation = validateAndConsumeConfirmationToken(input.confirmationToken, {
|
|
773
|
+
tool: 'noteplan_delete_folder',
|
|
774
|
+
target: confirmationTarget,
|
|
775
|
+
action: 'delete_folder',
|
|
776
|
+
});
|
|
777
|
+
if (!confirmation.ok) {
|
|
778
|
+
return {
|
|
779
|
+
success: false,
|
|
780
|
+
error: confirmationFailureMessage('noteplan_delete_folder', confirmation.reason),
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
const deleted = input.space
|
|
784
|
+
? store.deleteFolder({
|
|
785
|
+
space: input.space,
|
|
786
|
+
source: input.source || '',
|
|
787
|
+
})
|
|
788
|
+
: store.deleteFolder({
|
|
789
|
+
path: input.path || '',
|
|
790
|
+
});
|
|
791
|
+
return {
|
|
792
|
+
success: true,
|
|
793
|
+
message: deleted.source === 'space'
|
|
794
|
+
? `TeamSpace folder moved to @Trash`
|
|
795
|
+
: `Folder moved to @Trash`,
|
|
796
|
+
...deleted,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
catch (error) {
|
|
800
|
+
return {
|
|
801
|
+
success: false,
|
|
802
|
+
error: error instanceof Error ? error.message : 'Failed to delete folder',
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
export function renameFolder(params) {
|
|
807
|
+
const input = params ?? {};
|
|
808
|
+
try {
|
|
809
|
+
const preview = input.space
|
|
810
|
+
? store.previewRenameFolder({
|
|
811
|
+
space: input.space,
|
|
812
|
+
source: input.source || '',
|
|
813
|
+
newName: input.newName || '',
|
|
814
|
+
})
|
|
815
|
+
: store.previewRenameFolder({
|
|
816
|
+
sourcePath: input.sourcePath || '',
|
|
817
|
+
newName: input.newName || '',
|
|
818
|
+
});
|
|
819
|
+
const confirmationTarget = `${preview.fromPath}=>${preview.toPath}`;
|
|
820
|
+
if (input.dryRun === true) {
|
|
821
|
+
const token = issueConfirmationToken({
|
|
822
|
+
tool: 'noteplan_rename_folder',
|
|
823
|
+
target: confirmationTarget,
|
|
824
|
+
action: 'rename_folder',
|
|
825
|
+
});
|
|
826
|
+
return {
|
|
827
|
+
success: true,
|
|
828
|
+
dryRun: true,
|
|
829
|
+
message: `Dry run: folder ${preview.fromPath} would rename to ${preview.toPath}`,
|
|
830
|
+
...preview,
|
|
831
|
+
...token,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
const confirmation = validateAndConsumeConfirmationToken(input.confirmationToken, {
|
|
835
|
+
tool: 'noteplan_rename_folder',
|
|
836
|
+
target: confirmationTarget,
|
|
837
|
+
action: 'rename_folder',
|
|
838
|
+
});
|
|
839
|
+
if (!confirmation.ok) {
|
|
840
|
+
return {
|
|
841
|
+
success: false,
|
|
842
|
+
error: confirmationFailureMessage('noteplan_rename_folder', confirmation.reason),
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
const renamed = input.space
|
|
846
|
+
? store.renameFolder({
|
|
847
|
+
space: input.space,
|
|
848
|
+
source: input.source || '',
|
|
849
|
+
newName: input.newName || '',
|
|
850
|
+
})
|
|
851
|
+
: store.renameFolder({
|
|
852
|
+
sourcePath: input.sourcePath || '',
|
|
853
|
+
newName: input.newName || '',
|
|
854
|
+
});
|
|
855
|
+
return {
|
|
856
|
+
success: true,
|
|
857
|
+
message: renamed.source === 'space'
|
|
858
|
+
? `TeamSpace folder renamed to ${renamed.toPath}`
|
|
859
|
+
: `Folder renamed to ${renamed.toPath}`,
|
|
860
|
+
...renamed,
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
catch (error) {
|
|
864
|
+
return {
|
|
865
|
+
success: false,
|
|
866
|
+
error: error instanceof Error ? error.message : 'Failed to rename folder',
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
//# sourceMappingURL=spaces.js.map
|