@j0hanz/filesystem-mcp 2.1.4 → 2.1.6
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/cli.d.ts +2 -14
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -22
- package/dist/cli.js.map +1 -1
- package/dist/core/cursor.d.ts +2 -14
- package/dist/core/cursor.d.ts.map +1 -1
- package/dist/core/cursor.js +25 -39
- package/dist/core/cursor.js.map +1 -1
- package/dist/core/diff.d.ts +6 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +21 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/errors.d.ts +2 -31
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +15 -86
- package/dist/core/errors.js.map +1 -1
- package/dist/core/file-uri.d.ts +18 -2
- package/dist/core/file-uri.d.ts.map +1 -1
- package/dist/core/file-uri.js +34 -6
- package/dist/core/file-uri.js.map +1 -1
- package/dist/core/fmt.js +5 -5
- package/dist/core/fmt.js.map +1 -1
- package/dist/core/fs.d.ts +4 -13
- package/dist/core/fs.d.ts.map +1 -1
- package/dist/core/fs.js +4 -41
- package/dist/core/fs.js.map +1 -1
- package/dist/core/glob.d.ts +1 -7
- package/dist/core/glob.d.ts.map +1 -1
- package/dist/core/glob.js +1 -11
- package/dist/core/glob.js.map +1 -1
- package/dist/core/input-required.d.ts +2 -10
- package/dist/core/input-required.d.ts.map +1 -1
- package/dist/core/input-required.js +3 -12
- package/dist/core/input-required.js.map +1 -1
- package/dist/core/path-completer.d.ts.map +1 -1
- package/dist/core/path-completer.js +3 -7
- package/dist/core/path-completer.js.map +1 -1
- package/dist/core/path-discovery.js +2 -2
- package/dist/core/path-discovery.js.map +1 -1
- package/dist/core/path.d.ts +10 -25
- package/dist/core/path.d.ts.map +1 -1
- package/dist/core/path.js +28 -66
- package/dist/core/path.js.map +1 -1
- package/dist/core/primitives.d.ts +8 -3
- package/dist/core/primitives.d.ts.map +1 -1
- package/dist/core/primitives.js +10 -5
- package/dist/core/primitives.js.map +1 -1
- package/dist/core/read.d.ts +0 -16
- package/dist/core/read.d.ts.map +1 -1
- package/dist/core/read.js +19 -56
- package/dist/core/read.js.map +1 -1
- package/dist/core/schema.d.ts +1 -32
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +7 -66
- package/dist/core/schema.js.map +1 -1
- package/dist/core/search.d.ts +16 -5
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +118 -108
- package/dist/core/search.js.map +1 -1
- package/dist/core/sensitive.d.ts.map +1 -1
- package/dist/core/sensitive.js +7 -15
- package/dist/core/sensitive.js.map +1 -1
- package/dist/core/store.d.ts +1 -3
- package/dist/core/store.d.ts.map +1 -1
- package/dist/core/store.js +1 -5
- package/dist/core/store.js.map +1 -1
- package/dist/core/watcher-registry.d.ts +1 -8
- package/dist/core/watcher-registry.d.ts.map +1 -1
- package/dist/core/watcher-registry.js +3 -12
- package/dist/core/watcher-registry.js.map +1 -1
- package/dist/index.js +1 -8
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +7 -33
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +3 -9
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts +0 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -5
- package/dist/server.js.map +1 -1
- package/dist/tools/batch.d.ts +5 -69
- package/dist/tools/batch.d.ts.map +1 -1
- package/dist/tools/batch.js +7 -117
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/create.d.ts.map +1 -1
- package/dist/tools/create.js +8 -18
- package/dist/tools/create.js.map +1 -1
- package/dist/tools/define.d.ts +1 -4
- package/dist/tools/define.d.ts.map +1 -1
- package/dist/tools/define.js +6 -24
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/delete-file.d.ts.map +1 -1
- package/dist/tools/delete-file.js +6 -6
- package/dist/tools/delete-file.js.map +1 -1
- package/dist/tools/diff.d.ts.map +1 -1
- package/dist/tools/diff.js +3 -10
- package/dist/tools/diff.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +43 -87
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list.js +3 -2
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/move.d.ts.map +1 -1
- package/dist/tools/move.js +169 -119
- package/dist/tools/move.js.map +1 -1
- package/dist/tools/patch.d.ts.map +1 -1
- package/dist/tools/patch.js +5 -23
- package/dist/tools/patch.js.map +1 -1
- package/dist/tools/progress.d.ts +0 -1
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +2 -8
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +21 -25
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/replace-in-files.d.ts.map +1 -1
- package/dist/tools/replace-in-files.js +25 -44
- package/dist/tools/replace-in-files.js.map +1 -1
- package/dist/tools/search-content.d.ts.map +1 -1
- package/dist/tools/search-content.js +17 -49
- package/dist/tools/search-content.js.map +1 -1
- package/dist/tools/search-files.d.ts.map +1 -1
- package/dist/tools/search-files.js +1 -2
- package/dist/tools/search-files.js.map +1 -1
- package/dist/tools/stat.d.ts.map +1 -1
- package/dist/tools/stat.js +5 -6
- package/dist/tools/stat.js.map +1 -1
- package/dist/transport/http-policy.d.ts +6 -0
- package/dist/transport/http-policy.d.ts.map +1 -1
- package/dist/transport/http-policy.js +2 -2
- package/dist/transport/http-policy.js.map +1 -1
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +4 -11
- package/dist/transport/http.js.map +1 -1
- package/dist/transport/stdio.js +1 -1
- package/dist/transport/stdio.js.map +1 -1
- package/dist/transport.d.ts +1 -3
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +5 -2
- package/dist/transport.js.map +1 -1
- package/package.json +1 -1
package/dist/tools/index.js
CHANGED
|
@@ -27,7 +27,6 @@ export const ALL_TOOLS = [
|
|
|
27
27
|
GET_FILE_INFO,
|
|
28
28
|
];
|
|
29
29
|
export const MUTATING_TOOL_NAMES = new Set(ALL_TOOLS.filter((t) => !t.annotations.readOnlyHint).map((t) => t.name));
|
|
30
|
-
export const ALL_REGISTERED_TOOL_NAMES = ALL_TOOLS.map((t) => t.name);
|
|
31
30
|
/** The tools a server registers at this setting — the one owner of the `--read-only` gate. */
|
|
32
31
|
export function registeredTools(readOnly) {
|
|
33
32
|
return readOnly ? ALL_TOOLS.filter((t) => !MUTATING_TOOL_NAMES.has(t.name)) : ALL_TOOLS;
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM;IACN,WAAW;IACX,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,SAAS;IACT,kBAAkB;IAClB,wBAAwB;IACxB,cAAc;IACd,YAAY;IACZ,aAAa;CACL,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACxC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACxE,CAAC;AAEF,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM;IACN,WAAW;IACX,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,SAAS;IACT,kBAAkB;IAClB,wBAAwB;IACxB,cAAc;IACd,YAAY;IACZ,aAAa;CACL,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACxC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACxE,CAAC;AAEF,8FAA8F;AAC9F,MAAM,UAAU,eAAe,CAAC,QAAiB;IAC/C,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1F,CAAC;AAED,gFAAgF;AAChF,6EAA6E;AAC7E,2EAA2E;AAC3E,uEAAuE;AACvE,6CAA6C;AAC7C,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAUlG,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,MAAM,QAAQ,GAAG;QACf,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,aAAa;KAClC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC","sourcesContent":["import type { McpServer } from '@modelcontextprotocol/server';\n\nimport type { PageSnapshotStore } from '../core/page-store.js';\nimport type { PathGuard } from '../core/path.js';\nimport type { ResourceStore } from '../core/store.js';\nimport { CREATE } from './create.js';\nimport type { DefinedTool } from './define.js';\nimport { DELETE_FILE } from './delete-file.js';\nimport { DIFF } from './diff.js';\nimport { EDIT } from './edit.js';\nimport { LIST } from './list.js';\nimport { MOVE } from './move.js';\nimport { PATCH } from './patch.js';\nimport { READ_FILE } from './read.js';\nimport { SEARCH_AND_REPLACE } from './replace-in-files.js';\nimport { LIST_ALLOWED_DIRECTORIES } from './roots.js';\nimport { SEARCH_CONTENT } from './search-content.js';\nimport { SEARCH_FILES } from './search-files.js';\nimport { GET_FILE_INFO } from './stat.js';\n\nexport const ALL_TOOLS = [\n CREATE,\n DELETE_FILE,\n DIFF,\n EDIT,\n LIST,\n MOVE,\n PATCH,\n READ_FILE,\n SEARCH_AND_REPLACE,\n LIST_ALLOWED_DIRECTORIES,\n SEARCH_CONTENT,\n SEARCH_FILES,\n GET_FILE_INFO,\n] as const;\n\nexport const MUTATING_TOOL_NAMES = new Set(\n ALL_TOOLS.filter((t) => !t.annotations.readOnlyHint).map((t) => t.name),\n);\n\n/** The tools a server registers at this setting — the one owner of the `--read-only` gate. */\nexport function registeredTools(readOnly: boolean): readonly DefinedTool[] {\n return readOnly ? ALL_TOOLS.filter((t) => !MUTATING_TOOL_NAMES.has(t.name)) : ALL_TOOLS;\n}\n\n// Re-exported so documentation surfaces quote `.name` off the definition rather\n// than repeating the string. This module is the only owner of the inventory.\n// Only the read-only tools are named individually: the mutating six are no\n// longer listed by hand anywhere, so their names reach callers through\n// MUTATING_TOOL_NAMES and ALL_TOOLS instead.\nexport { LIST, LIST_ALLOWED_DIRECTORIES, READ_FILE, SEARCH_CONTENT, SEARCH_FILES, GET_FILE_INFO };\n\ninterface ToolRegistrarDeps {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n readonly pageStore: PageSnapshotStore;\n readonly resourceStore: ResourceStore;\n readonly readOnly?: boolean;\n}\n\nexport function registerTools(deps: ToolRegistrarDeps): void {\n const toolDeps = {\n server: deps.server,\n pathGuard: deps.pathGuard,\n pageStore: deps.pageStore,\n resourceStore: deps.resourceStore,\n };\n for (const tool of registeredTools(deps.readOnly ?? false)) {\n tool.register(toolDeps);\n }\n}\n"]}
|
package/dist/tools/list.js
CHANGED
|
@@ -3,8 +3,9 @@ import * as z from 'zod/v4';
|
|
|
3
3
|
import { timedSignal } from '../core/concurrency.js';
|
|
4
4
|
import { paginate } from '../core/cursor.js';
|
|
5
5
|
import { ErrorCode } from '../core/errors.js';
|
|
6
|
-
import { DEFAULT_EXCLUDE_PATTERNS, globEntries, isIgnoredByGitignore, loadRootGitignore,
|
|
6
|
+
import { DEFAULT_EXCLUDE_PATTERNS, globEntries, isIgnoredByGitignore, loadRootGitignore, } from '../core/glob.js';
|
|
7
7
|
import { toPosixRelative } from '../core/path.js';
|
|
8
|
+
import { resolveEntryType } from '../core/primitives.js';
|
|
8
9
|
import { CursorSchema, FileType as FileTypeEnum, includeHiddenField, includeIgnoredField, NextCursorSchema, NonNegInt, OptionalPath, PositiveInt, } from '../core/schema.js';
|
|
9
10
|
import { putJsonResource } from '../core/store.js';
|
|
10
11
|
import { DEFAULT_SEARCH_TIMEOUT_MS, DEFAULT_TREE_ENTRIES, MAX_LIST_ENTRIES, MAX_TREE_DEPTH, } from '../core/util.js';
|
|
@@ -62,7 +63,7 @@ async function collect(rootPath, options) {
|
|
|
62
63
|
})) {
|
|
63
64
|
continue;
|
|
64
65
|
}
|
|
65
|
-
const accessible = await options.pathGuard.isEntryAccessible(entry.path
|
|
66
|
+
const accessible = await options.pathGuard.isEntryAccessible(entry.path);
|
|
66
67
|
if (!accessible)
|
|
67
68
|
continue;
|
|
68
69
|
totalEntries++;
|
package/dist/tools/list.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { timedSignal } from '../core/concurrency.js';\nimport { paginate } from '../core/cursor.js';\nimport { ErrorCode } from '../core/errors.js';\nimport type { EntryType } from '../core/glob.js';\nimport {\n DEFAULT_EXCLUDE_PATTERNS,\n globEntries,\n isIgnoredByGitignore,\n loadRootGitignore,\n resolveEntryType,\n} from '../core/glob.js';\nimport type { PathGuard } from '../core/path.js';\nimport { toPosixRelative } from '../core/path.js';\nimport {\n CursorSchema,\n FileType as FileTypeEnum,\n includeHiddenField,\n includeIgnoredField,\n NextCursorSchema,\n NonNegInt,\n OptionalPath,\n PositiveInt,\n} from '../core/schema.js';\nimport type { JsonResourceResult } from '../core/store.js';\nimport { putJsonResource } from '../core/store.js';\nimport {\n DEFAULT_SEARCH_TIMEOUT_MS,\n DEFAULT_TREE_ENTRIES,\n MAX_LIST_ENTRIES,\n MAX_TREE_DEPTH,\n} from '../core/util.js';\nimport { defineTool, type ToolCtx } from './define.js';\n\ninterface CollectedEntry {\n name: string;\n relativePath: string; // POSIX\n type: EntryType;\n}\n\ninterface CollectOptions {\n maxDepth: number;\n includeHidden: boolean;\n includeIgnored: boolean;\n signal: AbortSignal;\n pathGuard: PathGuard;\n /** Upper bound on the stored, paginable entry array. */\n entryCap: number;\n onProgress?: (progress: { current: number; total?: number }) => void;\n}\n\ninterface CollectResult {\n entries: CollectedEntry[];\n totalEntries: number;\n totalFiles: number;\n totalDirectories: number;\n}\n\nfunction entryTypeRank(type: EntryType): number {\n if (type === 'directory') return 0;\n return 1;\n}\n\nfunction compareEntries(a: CollectedEntry, b: CollectedEntry): number {\n // Compare by parent directory first (depth-level grouping)\n const slashA = a.relativePath.lastIndexOf('/');\n const slashB = b.relativePath.lastIndexOf('/');\n const parentA = slashA === -1 ? '' : a.relativePath.slice(0, slashA);\n const parentB = slashB === -1 ? '' : b.relativePath.slice(0, slashB);\n\n if (parentA !== parentB) return parentA.localeCompare(parentB);\n\n // Within same parent: dirs first, then alphabetical\n const rankDiff = entryTypeRank(a.type) - entryTypeRank(b.type);\n if (rankDiff !== 0) return rankDiff;\n return a.name.localeCompare(b.name);\n}\n\nasync function collect(rootPath: string, options: CollectOptions): Promise<CollectResult> {\n const gitignoreMatcher = options.includeIgnored\n ? null\n : await loadRootGitignore(rootPath, options.signal);\n\n const entries: CollectedEntry[] = [];\n let scanned = 0;\n let totalEntries = 0;\n let totalFiles = 0;\n let totalDirectories = 0;\n\n for await (const entry of globEntries({\n cwd: rootPath,\n pattern: '**/*',\n excludePatterns: options.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,\n includeHidden: options.includeHidden,\n baseNameMatch: false,\n // ListInputSchema.maxDepth is 1-based (1 = top-level only); the shared\n // globEntries primitive is now 0-based, so subtract one here. options.maxDepth\n // is guaranteed >= 1 by its PositiveInt schema, so this never underflows.\n maxDepth: options.maxDepth - 1,\n onlyFiles: false,\n })) {\n options.signal.throwIfAborted();\n scanned++;\n options.onProgress?.({ current: scanned });\n\n const entryType: EntryType = resolveEntryType(entry.dirent);\n const isDir = entryType === 'directory';\n const relPath = toPosixRelative(rootPath, entry.path);\n const name = basename(relPath);\n\n if (\n gitignoreMatcher &&\n isIgnoredByGitignore(gitignoreMatcher, rootPath, entry.path, {\n isDirectory: isDir,\n })\n ) {\n continue;\n }\n const accessible = await options.pathGuard.isEntryAccessible(entry.path, entryType, [rootPath]);\n if (!accessible) continue;\n\n totalEntries++;\n if (entryType === 'directory') {\n totalDirectories++;\n } else {\n totalFiles++;\n }\n\n const collectedEntry: CollectedEntry = {\n name,\n relativePath: relPath,\n type: entryType,\n };\n\n if (entries.length < options.entryCap) {\n entries.push(collectedEntry);\n }\n }\n\n options.signal.throwIfAborted();\n entries.sort(compareEntries);\n\n return {\n entries,\n totalEntries,\n totalFiles,\n totalDirectories,\n };\n}\n\nconst TEE = '├── ';\nconst ELBOW = '└── ';\nconst PIPE = '│ ';\nconst INDENT = ' ';\n\nfunction renderMarkdown(rootName: string, entries: readonly CollectedEntry[]): string {\n if (entries.length === 0) return rootName;\n\n // Group entries by parent path\n const childrenOf = new Map<string, CollectedEntry[]>();\n for (const entry of entries) {\n const slash = entry.relativePath.lastIndexOf('/');\n const parentKey = slash === -1 ? '' : entry.relativePath.slice(0, slash);\n if (!childrenOf.has(parentKey)) childrenOf.set(parentKey, []);\n const children = childrenOf.get(parentKey);\n if (children) children.push(entry);\n }\n\n const lines: string[] = [rootName];\n\n function renderChildren(parentKey: string, prefix: string): void {\n const children = childrenOf.get(parentKey);\n if (!children) return;\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n if (!child) continue;\n const isLast = i === children.length - 1;\n const connector = isLast ? ELBOW : TEE;\n lines.push(prefix + connector + child.name);\n\n const childKey = child.relativePath;\n const nextPrefix = prefix + (isLast ? INDENT : PIPE);\n renderChildren(childKey, nextPrefix);\n }\n }\n\n renderChildren('', '');\n return lines.join('\\n');\n}\n\nconst DEFAULT_LIST_DEPTH = 1;\nconst DEFAULT_LIST_ENTRIES = DEFAULT_TREE_ENTRIES;\n\nconst ListInputSchema = z.strictObject({\n path: OptionalPath.describe('Directory to list (default: first allowed root)'),\n maxDepth: PositiveInt.max(MAX_TREE_DEPTH)\n .default(DEFAULT_LIST_DEPTH)\n .describe(\n `Max directory depth to traverse (default: ${String(DEFAULT_LIST_DEPTH)} = top-level only; increase to recurse deeper)`,\n ),\n maxEntries: PositiveInt.max(MAX_LIST_ENTRIES)\n .default(DEFAULT_LIST_ENTRIES)\n .describe(\n `Page size (default: ${String(DEFAULT_LIST_ENTRIES)}). Continue with nextCursor; an incomplete first page also carries resourceUri for the whole list.`,\n ),\n includeHidden: includeHiddenField(),\n includeIgnored: includeIgnoredField(),\n cursor: CursorSchema,\n});\n\nconst ListOutputSchema = z.strictObject({\n path: z.string().optional().describe('Resolved absolute path of the listed directory'),\n entries: z\n .array(\n z.strictObject({\n name: z.string().describe('Entry basename'),\n relativePath: z.string().describe('POSIX path relative to the listed directory'),\n type: FileTypeEnum.describe('Entry type: file, directory, symlink, or other'),\n }),\n )\n .describe('Inline directory entries sorted directories-first then alphabetically by name'),\n // No `markdown` field: the ASCII tree is the call's text content already, and\n // carrying it here too doubled every list response for a string the client\n // has in hand. The stored full-tree resource still holds its own copy — that\n // one is a *different* (uncapped) tree and is never sent inline.\n entryCount: NonNegInt.describe('Number of entries included in this response'),\n totalEntries: NonNegInt.describe('Total entries found before the maxEntries cap was applied'),\n totalFiles: NonNegInt.describe('Total number of files found'),\n totalDirectories: NonNegInt.describe('Total number of directories found'),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'URI to the full entry list in the resource store; first page only, whenever the response is ' +\n 'incomplete — more pages follow, or the hard cap cut the listing. The stored list is itself ' +\n 'bounded by that cap and marked truncated if exceeded.',\n ),\n nextCursor: NextCursorSchema,\n});\n\ninterface ListPageMetadata {\n readonly path: string;\n readonly totalEntries: number;\n readonly totalFiles: number;\n readonly totalDirectories: number;\n}\n\nfunction listQueryKey(args: z.infer<typeof ListInputSchema>, path: string): string {\n return JSON.stringify({\n method: 'list',\n path,\n maxDepth: args.maxDepth,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\n });\n}\n\nfunction listOutput(\n entries: readonly CollectedEntry[],\n metadata: ListPageMetadata,\n nextCursor: string | undefined,\n resourceUri: string | undefined,\n): z.infer<typeof ListOutputSchema> {\n return {\n path: metadata.path,\n entries: [...entries],\n entryCount: entries.length,\n totalEntries: metadata.totalEntries,\n totalFiles: metadata.totalFiles,\n totalDirectories: metadata.totalDirectories,\n ...(resourceUri !== undefined ? { resourceUri } : {}),\n ...(nextCursor !== undefined ? { nextCursor } : {}),\n };\n}\n\nasync function handleList(\n args: z.infer<typeof ListInputSchema>,\n ctx: ToolCtx,\n): Promise<{\n structured: z.infer<typeof ListOutputSchema>;\n markdown: string;\n link?: ContentBlock;\n}> {\n const path = args.path;\n const resolvedPath = ctx.fs.pathGuard.resolvePathOrRoot(path);\n const queryKey = listQueryKey(args, resolvedPath);\n const { resourceStore } = ctx;\n\n const paged = await paginate<CollectedEntry, ListPageMetadata, JsonResourceResult>({\n store: ctx.pageStore,\n queryKey,\n cursor: args.cursor,\n pageSize: args.maxEntries,\n produce: async () => {\n const validDir = await ctx.fs.pathGuard.validateExistingDirectory(resolvedPath);\n const result = await collect(validDir, {\n maxDepth: args.maxDepth,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\n signal: timedSignal(ctx.signal, DEFAULT_SEARCH_TIMEOUT_MS),\n pathGuard: ctx.fs.pathGuard,\n entryCap: MAX_LIST_ENTRIES,\n ...(ctx.onProgress ? { onProgress: ctx.onProgress } : {}),\n });\n return {\n items: result.entries,\n metadata: {\n path: validDir,\n totalEntries: result.totalEntries,\n totalFiles: result.totalFiles,\n totalDirectories: result.totalDirectories,\n },\n truncated: result.totalEntries > result.entries.length,\n };\n },\n externalize: resourceStore\n ? (entries, metadata) => {\n const name = basename(metadata.path);\n // The stored tree is the whole collected set — itself bounded by the\n // hard cap, and marked when the cap cut it.\n const fullOutput = {\n entries,\n markdown: renderMarkdown(name, entries),\n totalEntries: metadata.totalEntries,\n totalFiles: metadata.totalFiles,\n totalDirectories: metadata.totalDirectories,\n ...(metadata.totalEntries > entries.length ? { truncated: true } : {}),\n };\n return putJsonResource(resourceStore, `${name} tree`, fullOutput);\n }\n : undefined,\n });\n\n return {\n structured: listOutput(paged.page, paged.metadata, paged.nextCursor, paged.resource?.entry.uri),\n markdown: renderMarkdown(basename(paged.metadata.path), [...paged.page]),\n ...(paged.resource ? { link: paged.resource.link } : {}),\n };\n}\n\nexport const LIST = defineTool({\n name: 'list',\n title: 'List',\n description:\n 'List sorted directory entries and an ASCII tree. maxDepth=1 is top-level. ' +\n 'maxEntries sets page size; continue with nextCursor. An incomplete first page also carries resourceUri for the whole list.',\n input: ListInputSchema,\n output: ListOutputSchema,\n // Not published: every field is a plainly-named scalar (`entryCount`,\n // `totalFiles`, `nextCursor`) that one sample response teaches, and the\n // schema costs 1599 chars of every session start. Publishing is reserved for\n // the value-XOR-error union shape a sample cannot convey.\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.NOT_DIRECTORY,\n progress: (args) => ({\n label: 'List',\n subject: args.path ? basename(args.path) : '.',\n }),\n accessPaths: (args) => (args.path ? [args.path] : []),\n run: async (args, ctx) => {\n const { structured, markdown, link } = await handleList(args, ctx);\n // The tree is what the model reads; nextCursor and the full-list URI used\n // to reach it only through the structured half, which now ships as _meta.\n // Both ride the text so paging needs no second lookup.\n const trailer: string[] = [];\n if (structured.nextCursor !== undefined) {\n trailer.push(`nextCursor: ${structured.nextCursor}`);\n }\n if (structured.resourceUri !== undefined) {\n trailer.push(\n `${String(structured.entryCount)} of ${String(structured.totalEntries)} entries shown; full tree at ${structured.resourceUri}`,\n );\n }\n return {\n structured,\n text: trailer.length > 0 ? `${markdown}\\n\\n${trailer.join('\\n')}` : markdown,\n ...(link ? { resources: [link] } : {}),\n };\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { timedSignal } from '../core/concurrency.js';\nimport { paginate } from '../core/cursor.js';\nimport { ErrorCode } from '../core/errors.js';\nimport type { EntryType } from '../core/glob.js';\nimport {\n DEFAULT_EXCLUDE_PATTERNS,\n globEntries,\n isIgnoredByGitignore,\n loadRootGitignore,\n} from '../core/glob.js';\nimport type { PathGuard } from '../core/path.js';\nimport { toPosixRelative } from '../core/path.js';\nimport { resolveEntryType } from '../core/primitives.js';\nimport {\n CursorSchema,\n FileType as FileTypeEnum,\n includeHiddenField,\n includeIgnoredField,\n NextCursorSchema,\n NonNegInt,\n OptionalPath,\n PositiveInt,\n} from '../core/schema.js';\nimport type { JsonResourceResult } from '../core/store.js';\nimport { putJsonResource } from '../core/store.js';\nimport {\n DEFAULT_SEARCH_TIMEOUT_MS,\n DEFAULT_TREE_ENTRIES,\n MAX_LIST_ENTRIES,\n MAX_TREE_DEPTH,\n} from '../core/util.js';\nimport { defineTool, type ToolCtx } from './define.js';\n\ninterface CollectedEntry {\n name: string;\n relativePath: string; // POSIX\n type: EntryType;\n}\n\ninterface CollectOptions {\n maxDepth: number;\n includeHidden: boolean;\n includeIgnored: boolean;\n signal: AbortSignal;\n pathGuard: PathGuard;\n /** Upper bound on the stored, paginable entry array. */\n entryCap: number;\n onProgress?: (progress: { current: number; total?: number }) => void;\n}\n\ninterface CollectResult {\n entries: CollectedEntry[];\n totalEntries: number;\n totalFiles: number;\n totalDirectories: number;\n}\n\nfunction entryTypeRank(type: EntryType): number {\n if (type === 'directory') return 0;\n return 1;\n}\n\nfunction compareEntries(a: CollectedEntry, b: CollectedEntry): number {\n // Compare by parent directory first (depth-level grouping)\n const slashA = a.relativePath.lastIndexOf('/');\n const slashB = b.relativePath.lastIndexOf('/');\n const parentA = slashA === -1 ? '' : a.relativePath.slice(0, slashA);\n const parentB = slashB === -1 ? '' : b.relativePath.slice(0, slashB);\n\n if (parentA !== parentB) return parentA.localeCompare(parentB);\n\n // Within same parent: dirs first, then alphabetical\n const rankDiff = entryTypeRank(a.type) - entryTypeRank(b.type);\n if (rankDiff !== 0) return rankDiff;\n return a.name.localeCompare(b.name);\n}\n\nasync function collect(rootPath: string, options: CollectOptions): Promise<CollectResult> {\n const gitignoreMatcher = options.includeIgnored\n ? null\n : await loadRootGitignore(rootPath, options.signal);\n\n const entries: CollectedEntry[] = [];\n let scanned = 0;\n let totalEntries = 0;\n let totalFiles = 0;\n let totalDirectories = 0;\n\n for await (const entry of globEntries({\n cwd: rootPath,\n pattern: '**/*',\n excludePatterns: options.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,\n includeHidden: options.includeHidden,\n baseNameMatch: false,\n // ListInputSchema.maxDepth is 1-based (1 = top-level only); the shared\n // globEntries primitive is now 0-based, so subtract one here. options.maxDepth\n // is guaranteed >= 1 by its PositiveInt schema, so this never underflows.\n maxDepth: options.maxDepth - 1,\n onlyFiles: false,\n })) {\n options.signal.throwIfAborted();\n scanned++;\n options.onProgress?.({ current: scanned });\n\n const entryType: EntryType = resolveEntryType(entry.dirent);\n const isDir = entryType === 'directory';\n const relPath = toPosixRelative(rootPath, entry.path);\n const name = basename(relPath);\n\n if (\n gitignoreMatcher &&\n isIgnoredByGitignore(gitignoreMatcher, rootPath, entry.path, {\n isDirectory: isDir,\n })\n ) {\n continue;\n }\n const accessible = await options.pathGuard.isEntryAccessible(entry.path);\n if (!accessible) continue;\n\n totalEntries++;\n if (entryType === 'directory') {\n totalDirectories++;\n } else {\n totalFiles++;\n }\n\n const collectedEntry: CollectedEntry = {\n name,\n relativePath: relPath,\n type: entryType,\n };\n\n if (entries.length < options.entryCap) {\n entries.push(collectedEntry);\n }\n }\n\n options.signal.throwIfAborted();\n entries.sort(compareEntries);\n\n return {\n entries,\n totalEntries,\n totalFiles,\n totalDirectories,\n };\n}\n\nconst TEE = '├── ';\nconst ELBOW = '└── ';\nconst PIPE = '│ ';\nconst INDENT = ' ';\n\nfunction renderMarkdown(rootName: string, entries: readonly CollectedEntry[]): string {\n if (entries.length === 0) return rootName;\n\n // Group entries by parent path\n const childrenOf = new Map<string, CollectedEntry[]>();\n for (const entry of entries) {\n const slash = entry.relativePath.lastIndexOf('/');\n const parentKey = slash === -1 ? '' : entry.relativePath.slice(0, slash);\n if (!childrenOf.has(parentKey)) childrenOf.set(parentKey, []);\n const children = childrenOf.get(parentKey);\n if (children) children.push(entry);\n }\n\n const lines: string[] = [rootName];\n\n function renderChildren(parentKey: string, prefix: string): void {\n const children = childrenOf.get(parentKey);\n if (!children) return;\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n if (!child) continue;\n const isLast = i === children.length - 1;\n const connector = isLast ? ELBOW : TEE;\n lines.push(prefix + connector + child.name);\n\n const childKey = child.relativePath;\n const nextPrefix = prefix + (isLast ? INDENT : PIPE);\n renderChildren(childKey, nextPrefix);\n }\n }\n\n renderChildren('', '');\n return lines.join('\\n');\n}\n\nconst DEFAULT_LIST_DEPTH = 1;\nconst DEFAULT_LIST_ENTRIES = DEFAULT_TREE_ENTRIES;\n\nconst ListInputSchema = z.strictObject({\n path: OptionalPath.describe('Directory to list (default: first allowed root)'),\n maxDepth: PositiveInt.max(MAX_TREE_DEPTH)\n .default(DEFAULT_LIST_DEPTH)\n .describe(\n `Max directory depth to traverse (default: ${String(DEFAULT_LIST_DEPTH)} = top-level only; increase to recurse deeper)`,\n ),\n maxEntries: PositiveInt.max(MAX_LIST_ENTRIES)\n .default(DEFAULT_LIST_ENTRIES)\n .describe(\n `Page size (default: ${String(DEFAULT_LIST_ENTRIES)}). Continue with nextCursor; an incomplete first page also carries resourceUri for the whole list.`,\n ),\n includeHidden: includeHiddenField(),\n includeIgnored: includeIgnoredField(),\n cursor: CursorSchema,\n});\n\nconst ListOutputSchema = z.strictObject({\n path: z.string().optional().describe('Resolved absolute path of the listed directory'),\n entries: z\n .array(\n z.strictObject({\n name: z.string().describe('Entry basename'),\n relativePath: z.string().describe('POSIX path relative to the listed directory'),\n type: FileTypeEnum.describe('Entry type: file, directory, symlink, or other'),\n }),\n )\n .describe('Inline directory entries sorted directories-first then alphabetically by name'),\n // No `markdown` field: the ASCII tree is the call's text content already, and\n // carrying it here too doubled every list response for a string the client\n // has in hand. The stored full-tree resource still holds its own copy — that\n // one is a *different* (uncapped) tree and is never sent inline.\n entryCount: NonNegInt.describe('Number of entries included in this response'),\n totalEntries: NonNegInt.describe('Total entries found before the maxEntries cap was applied'),\n totalFiles: NonNegInt.describe('Total number of files found'),\n totalDirectories: NonNegInt.describe('Total number of directories found'),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'URI to the full entry list in the resource store; first page only, whenever the response is ' +\n 'incomplete — more pages follow, or the hard cap cut the listing. The stored list is itself ' +\n 'bounded by that cap and marked truncated if exceeded.',\n ),\n nextCursor: NextCursorSchema,\n});\n\ninterface ListPageMetadata {\n readonly path: string;\n readonly totalEntries: number;\n readonly totalFiles: number;\n readonly totalDirectories: number;\n}\n\nfunction listQueryKey(args: z.infer<typeof ListInputSchema>, path: string): string {\n return JSON.stringify({\n method: 'list',\n path,\n maxDepth: args.maxDepth,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\n });\n}\n\nfunction listOutput(\n entries: readonly CollectedEntry[],\n metadata: ListPageMetadata,\n nextCursor: string | undefined,\n resourceUri: string | undefined,\n): z.infer<typeof ListOutputSchema> {\n return {\n path: metadata.path,\n entries: [...entries],\n entryCount: entries.length,\n totalEntries: metadata.totalEntries,\n totalFiles: metadata.totalFiles,\n totalDirectories: metadata.totalDirectories,\n ...(resourceUri !== undefined ? { resourceUri } : {}),\n ...(nextCursor !== undefined ? { nextCursor } : {}),\n };\n}\n\nasync function handleList(\n args: z.infer<typeof ListInputSchema>,\n ctx: ToolCtx,\n): Promise<{\n structured: z.infer<typeof ListOutputSchema>;\n markdown: string;\n link?: ContentBlock;\n}> {\n const path = args.path;\n const resolvedPath = ctx.fs.pathGuard.resolvePathOrRoot(path);\n const queryKey = listQueryKey(args, resolvedPath);\n const { resourceStore } = ctx;\n\n const paged = await paginate<CollectedEntry, ListPageMetadata, JsonResourceResult>({\n store: ctx.pageStore,\n queryKey,\n cursor: args.cursor,\n pageSize: args.maxEntries,\n produce: async () => {\n const validDir = await ctx.fs.pathGuard.validateExistingDirectory(resolvedPath);\n const result = await collect(validDir, {\n maxDepth: args.maxDepth,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\n signal: timedSignal(ctx.signal, DEFAULT_SEARCH_TIMEOUT_MS),\n pathGuard: ctx.fs.pathGuard,\n entryCap: MAX_LIST_ENTRIES,\n ...(ctx.onProgress ? { onProgress: ctx.onProgress } : {}),\n });\n return {\n items: result.entries,\n metadata: {\n path: validDir,\n totalEntries: result.totalEntries,\n totalFiles: result.totalFiles,\n totalDirectories: result.totalDirectories,\n },\n truncated: result.totalEntries > result.entries.length,\n };\n },\n externalize: resourceStore\n ? (entries, metadata) => {\n const name = basename(metadata.path);\n // The stored tree is the whole collected set — itself bounded by the\n // hard cap, and marked when the cap cut it.\n const fullOutput = {\n entries,\n markdown: renderMarkdown(name, entries),\n totalEntries: metadata.totalEntries,\n totalFiles: metadata.totalFiles,\n totalDirectories: metadata.totalDirectories,\n ...(metadata.totalEntries > entries.length ? { truncated: true } : {}),\n };\n return putJsonResource(resourceStore, `${name} tree`, fullOutput);\n }\n : undefined,\n });\n\n return {\n structured: listOutput(paged.page, paged.metadata, paged.nextCursor, paged.resource?.entry.uri),\n markdown: renderMarkdown(basename(paged.metadata.path), [...paged.page]),\n ...(paged.resource ? { link: paged.resource.link } : {}),\n };\n}\n\nexport const LIST = defineTool({\n name: 'list',\n title: 'List',\n description:\n 'List sorted directory entries and an ASCII tree. maxDepth=1 is top-level. ' +\n 'maxEntries sets page size; continue with nextCursor. An incomplete first page also carries resourceUri for the whole list.',\n input: ListInputSchema,\n output: ListOutputSchema,\n // Not published: every field is a plainly-named scalar (`entryCount`,\n // `totalFiles`, `nextCursor`) that one sample response teaches, and the\n // schema costs 1599 chars of every session start. Publishing is reserved for\n // the value-XOR-error union shape a sample cannot convey.\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.NOT_DIRECTORY,\n progress: (args) => ({\n label: 'List',\n subject: args.path ? basename(args.path) : '.',\n }),\n accessPaths: (args) => (args.path ? [args.path] : []),\n run: async (args, ctx) => {\n const { structured, markdown, link } = await handleList(args, ctx);\n // The tree is what the model reads; nextCursor and the full-list URI used\n // to reach it only through the structured half, which now ships as _meta.\n // Both ride the text so paging needs no second lookup.\n const trailer: string[] = [];\n if (structured.nextCursor !== undefined) {\n trailer.push(`nextCursor: ${structured.nextCursor}`);\n }\n if (structured.resourceUri !== undefined) {\n trailer.push(\n `${String(structured.entryCount)} of ${String(structured.totalEntries)} entries shown; full tree at ${structured.resourceUri}`,\n );\n }\n return {\n structured,\n text: trailer.length > 0 ? `${markdown}\\n\\n${trailer.join('\\n')}` : markdown,\n ...(link ? { resources: [link] } : {}),\n };\n },\n});\n"]}
|
package/dist/tools/move.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"AA8eA,eAAO,MAAM,IAAI,mCA8Cf,CAAC"}
|
package/dist/tools/move.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { isInputRequiredResult } from '@modelcontextprotocol/server';
|
|
2
|
-
import { basename, dirname, resolve
|
|
2
|
+
import { basename, dirname, resolve } from 'node:path';
|
|
3
3
|
import * as z from 'zod/v4';
|
|
4
|
+
import { processInParallel } from '../core/concurrency.js';
|
|
4
5
|
import { ErrorCode, FsError, isFsError, isNodeError, Problem, rethrowIfAborted, } from '../core/errors.js';
|
|
5
6
|
import { joinRoster, pathLabel } from '../core/fmt.js';
|
|
6
7
|
import { destExists } from '../core/fs.js';
|
|
7
|
-
import { readAcceptedChoice } from '../core/input-required.js';
|
|
8
|
+
import { choiceInput, pendingRoundTrip, readAcceptedChoice } from '../core/input-required.js';
|
|
8
9
|
import { IS_CASE_INSENSITIVE_FS, isPathInsideDirectory, isSamePath } from '../core/path-utils.js';
|
|
9
|
-
import { defaultFalseBoolean,
|
|
10
|
-
import {
|
|
10
|
+
import { defaultFalseBoolean, PerFileErrorSchema, RequiredPath } from '../core/schema.js';
|
|
11
|
+
import { PARALLEL_CONCURRENCY } from '../core/util.js';
|
|
11
12
|
import { defineTool } from './define.js';
|
|
12
13
|
const MoveItemSchema = z.strictObject({
|
|
13
14
|
source: RequiredPath.describe('Path of the file or directory to move or copy'),
|
|
@@ -22,7 +23,11 @@ const MoveInputSchema = z.strictObject({
|
|
|
22
23
|
copy: defaultFalseBoolean('Copy instead of move; sources are left in place'),
|
|
23
24
|
overwrite: defaultFalseBoolean('Copy mode only: overwrite existing destinations without confirmation'),
|
|
24
25
|
});
|
|
25
|
-
const MoveFailureItemSchema =
|
|
26
|
+
const MoveFailureItemSchema = z.strictObject({
|
|
27
|
+
source: z.string().describe('Source path that could not be moved'),
|
|
28
|
+
destination: z.string().describe('Intended destination path for the failed move'),
|
|
29
|
+
error: PerFileErrorSchema,
|
|
30
|
+
});
|
|
26
31
|
const MoveOutputSchema = z.strictObject({
|
|
27
32
|
moves: z.array(MoveItemResultSchema).describe('Successfully completed operations'),
|
|
28
33
|
failures: z
|
|
@@ -34,64 +39,61 @@ const MoveOutputSchema = z.strictObject({
|
|
|
34
39
|
.optional()
|
|
35
40
|
.describe('Destinations skipped because the user chose Skip'),
|
|
36
41
|
});
|
|
42
|
+
const VERB = { move: 'Move', copy: 'Copy' };
|
|
37
43
|
/**
|
|
38
|
-
* Phase 1 (no mutation): validate source and destination, reject self-
|
|
39
|
-
*
|
|
40
|
-
* whether this
|
|
41
|
-
* the old flow created the destination's parent before asking, which
|
|
42
|
-
* the filesystem before a confirmation; that now waits for phase 2 (R14).
|
|
44
|
+
* Phase 1 (no mutation): validate source and destination, reject self-targets
|
|
45
|
+
* and transfers into a subdirectory of the source, and stat the destination to
|
|
46
|
+
* decide whether this pair needs an overwrite confirmation. No `mkdir` happens
|
|
47
|
+
* here — the old flow created the destination's parent before asking, which
|
|
48
|
+
* mutated the filesystem before a confirmation; that now waits for phase 2 (R14).
|
|
49
|
+
*
|
|
50
|
+
* `overwrite` is copy-only — move has no confirmation bypass — so a move always
|
|
51
|
+
* passes false.
|
|
43
52
|
*/
|
|
44
|
-
async function
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
-
try {
|
|
48
|
-
({ renamePath, realPath } = await validateMoveSource(move.source, fs));
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
return { status: 'fail', failure: pairFailure(move, error) };
|
|
52
|
-
}
|
|
53
|
+
async function planTransfer(op, pair, fs, overwrite) {
|
|
54
|
+
let realSource;
|
|
55
|
+
let opSource;
|
|
53
56
|
let validDest;
|
|
54
57
|
try {
|
|
55
|
-
|
|
58
|
+
({ realSource, opSource } = await validateTransferSource(op, pair.source, fs));
|
|
59
|
+
validDest = await fs.pathGuard.validatePathForWrite(pair.destination);
|
|
56
60
|
}
|
|
57
61
|
catch (error) {
|
|
58
|
-
return { status: 'fail', failure: pairFailure(
|
|
62
|
+
return { status: 'fail', failure: pairFailure(pair, error) };
|
|
59
63
|
}
|
|
60
|
-
// Comparisons run on the resolved source; only the fs call
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
const resolvedSource = resolve(
|
|
64
|
+
// Comparisons run on the resolved source; only the fs call in phase 2 uses
|
|
65
|
+
// opSource. validatePathForWrite resolves the destination through a symlink
|
|
66
|
+
// too, so both sides of every check must be resolved to match.
|
|
67
|
+
const resolvedSource = resolve(realSource);
|
|
64
68
|
const resolvedDest = resolve(validDest);
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
const isSelf = resolvedSource === resolvedDest;
|
|
70
|
+
const isCaseOnlyRename = !isSelf && isSamePath(resolvedSource, resolvedDest);
|
|
71
|
+
// A copy onto a case-only variant of its own source is the same file, so it
|
|
72
|
+
// is a no-op; the same rename is real work for move and proceeds.
|
|
73
|
+
if (isSelf || (isCaseOnlyRename && op === 'copy')) {
|
|
67
74
|
return { status: 'noop' };
|
|
68
75
|
}
|
|
69
|
-
|
|
70
|
-
const normalizedSource = IS_CASE_INSENSITIVE_FS
|
|
71
|
-
? (resolvedSource + sep).toLowerCase()
|
|
72
|
-
: resolvedSource + sep;
|
|
73
|
-
if (normalizedDest.startsWith(normalizedSource)) {
|
|
76
|
+
if (!isCaseOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {
|
|
74
77
|
return {
|
|
75
78
|
status: 'fail',
|
|
76
|
-
failure: pairFailure(
|
|
79
|
+
failure: pairFailure(pair, new FsError(ErrorCode.INVALID_INPUT, `Cannot ${op} a directory into its own subdirectory`, pair.source)),
|
|
77
80
|
};
|
|
78
81
|
}
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const pending = !isCaseOnlyRename && destExistedOriginally;
|
|
82
|
+
const destExistedOriginally = !isCaseOnlyRename && (await destExists(fs, validDest, op));
|
|
83
|
+
const pending = destExistedOriginally && !overwrite;
|
|
82
84
|
return {
|
|
83
85
|
status: 'plan',
|
|
84
|
-
plan: { pair
|
|
86
|
+
plan: { pair, opSource, validDest, isCaseOnlyRename, destExistedOriginally, pending },
|
|
85
87
|
};
|
|
86
88
|
}
|
|
87
89
|
/**
|
|
88
|
-
* Phase 2 (mutation): confirm an overwrite if needed,
|
|
89
|
-
*
|
|
90
|
-
* A declined/missing confirmation throws `CANCELLED`, collected as a
|
|
91
|
-
* failure by the caller. A destination that appeared between plan and
|
|
92
|
-
* (created during the confirmation gap) also fails closed.
|
|
90
|
+
* Phase 2 (mutation): confirm an overwrite if needed, TOCTOU-check the
|
|
91
|
+
* destination, create its parent, then rename (with cross-device fallback) or
|
|
92
|
+
* copy. A declined/missing confirmation throws `CANCELLED`, collected as a
|
|
93
|
+
* per-pair failure by the caller. A destination that appeared between plan and
|
|
94
|
+
* mutation (created during the confirmation gap) also fails closed.
|
|
93
95
|
*/
|
|
94
|
-
async function
|
|
96
|
+
async function executeTransfer(op, plan, ctx, pendingSorted) {
|
|
95
97
|
if (plan.pending) {
|
|
96
98
|
const key = `confirm_${pendingSorted.indexOf(plan.validDest)}`;
|
|
97
99
|
const choice = readAcceptedChoice(ctx.inputResponses, key);
|
|
@@ -99,78 +101,130 @@ async function executeMove(plan, ctx, pendingSorted) {
|
|
|
99
101
|
return { skipped: plan.pair.destination };
|
|
100
102
|
}
|
|
101
103
|
if (choice !== 'overwrite') {
|
|
102
|
-
throw new FsError(ErrorCode.CANCELLED,
|
|
104
|
+
throw new FsError(ErrorCode.CANCELLED, `${VERB[op]} cancelled: overwrite of "${plan.pair.destination}" was declined or missing`, plan.pair.destination);
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
// TOCTOU check before any mutation: a destination that did not exist when
|
|
106
108
|
// planned but exists now was created during the confirmation gap.
|
|
107
|
-
if (!plan.isCaseOnlyRename
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
109
|
+
if (!plan.isCaseOnlyRename &&
|
|
110
|
+
!plan.destExistedOriginally &&
|
|
111
|
+
(await destExists(ctx.fs, plan.validDest, op))) {
|
|
112
|
+
throw new FsError(ErrorCode.CANCELLED, `${VERB[op]} cancelled: destination "${plan.pair.destination}" was created during confirmation.`, plan.pair.destination);
|
|
112
113
|
}
|
|
113
114
|
await ctx.fs.mkdir(dirname(plan.validDest), { recursive: true });
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
async function planCopy(copy, fs, overwrite) {
|
|
119
|
-
let realSource;
|
|
120
|
-
try {
|
|
121
|
-
realSource = await fs.pathGuard.validateExistingPath(copy.source);
|
|
115
|
+
if (op === 'move') {
|
|
116
|
+
await performRenameWithFallback(plan.opSource, plan.validDest, ctx.fs, plan.pair.source);
|
|
117
|
+
ctx.log?.('info', `move: ${plan.pair.source} -> ${plan.pair.destination}`, 'move');
|
|
122
118
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
119
|
+
else {
|
|
120
|
+
await ctx.fs.cp(plan.opSource, plan.validDest, {
|
|
121
|
+
recursive: true,
|
|
122
|
+
verbatimSymlinks: true,
|
|
123
|
+
preserveTimestamps: true,
|
|
124
|
+
force: true,
|
|
125
|
+
});
|
|
129
126
|
}
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
return { value: { from: plan.opSource, to: plan.validDest } };
|
|
128
|
+
}
|
|
129
|
+
function pairFailure(pair, error) {
|
|
130
|
+
return {
|
|
131
|
+
source: pair.source,
|
|
132
|
+
destination: pair.destination,
|
|
133
|
+
error: Problem.fromUnknown(error, ErrorCode.UNKNOWN, pair.source),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The source→destination sibling of `runOverPaths`: plan every pair, fail closed
|
|
138
|
+
* on a duplicated destination, round-trip the overwrite confirmations as one
|
|
139
|
+
* set, then execute what survived in parallel. `copy` and `move` differ only in
|
|
140
|
+
* their `plan` and `execute` callbacks.
|
|
141
|
+
*/
|
|
142
|
+
async function runTransfers(items, ctx, op, overwrite) {
|
|
143
|
+
const { results: planned, errors: planErrors } = await processInParallel(items, (pair) => planTransfer(op, pair, ctx.fs, overwrite), PARALLEL_CONCURRENCY, ctx.signal);
|
|
144
|
+
// plan callbacks are fail-closed (they return { status: 'fail' }, never
|
|
145
|
+
// throw), so planErrors should always be empty. Surface the first rather
|
|
146
|
+
// than silently dropping the item if a future plan callback violates that.
|
|
147
|
+
const firstPlanError = planErrors[0];
|
|
148
|
+
if (firstPlanError)
|
|
149
|
+
throw firstPlanError.error;
|
|
150
|
+
const failures = [];
|
|
151
|
+
const candidates = [];
|
|
152
|
+
for (const { value } of planned) {
|
|
153
|
+
if (value.status === 'fail')
|
|
154
|
+
failures.push(value.failure);
|
|
155
|
+
else if (value.status === 'plan')
|
|
156
|
+
candidates.push(value.plan);
|
|
157
|
+
// 'noop' (self-copy / self-move) is silently skipped.
|
|
132
158
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
159
|
+
// Two sources targeting the same destination in one batch would otherwise
|
|
160
|
+
// collapse to a single shared overwrite confirmation and let the second
|
|
161
|
+
// one silently clobber the first's freshly-written content. Fail closed:
|
|
162
|
+
// only the first plan per destination proceeds; later ones targeting the
|
|
163
|
+
// same destination are reported as a per-item failure.
|
|
164
|
+
const seenDest = new Set();
|
|
165
|
+
const ready = [];
|
|
166
|
+
for (const candidate of candidates) {
|
|
167
|
+
const destKey = IS_CASE_INSENSITIVE_FS
|
|
168
|
+
? candidate.validDest.toLowerCase()
|
|
169
|
+
: candidate.validDest;
|
|
170
|
+
if (seenDest.has(destKey)) {
|
|
171
|
+
failures.push(pairFailure(candidate.pair, new FsError(ErrorCode.INVALID_INPUT, `${VERB[op]} cancelled: another entry in this batch already targets destination "${candidate.pair.destination}"`, candidate.pair.destination)));
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
seenDest.add(destKey);
|
|
175
|
+
ready.push(candidate);
|
|
136
176
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
177
|
+
const pendingSorted = ready
|
|
178
|
+
.filter((p) => p.pending)
|
|
179
|
+
.map((p) => p.validDest)
|
|
180
|
+
.sort();
|
|
181
|
+
if (pendingSorted.length > 0) {
|
|
182
|
+
// Round 1 returns input_required; a retry whose verified state does not
|
|
183
|
+
// bind this overwrite set throws (R9) via `pendingRoundTrip`.
|
|
184
|
+
const round = await pendingRoundTrip({
|
|
185
|
+
op,
|
|
186
|
+
pending: pendingSorted,
|
|
187
|
+
requestState: ctx.requestState,
|
|
188
|
+
clientCapabilities: ctx.clientCapabilities,
|
|
189
|
+
buildInputs: (dests) => dests.map((dest, i) => choiceInput(`confirm_${i}`, op === 'move'
|
|
190
|
+
? `"${dest}" already exists. Overwrite it?`
|
|
191
|
+
: `Destination "${dest}" already exists. Overwrite it?`, [
|
|
192
|
+
{ value: 'overwrite', title: 'Overwrite' },
|
|
193
|
+
{ value: 'skip', title: 'Skip' },
|
|
194
|
+
])),
|
|
195
|
+
});
|
|
196
|
+
if (round !== undefined)
|
|
197
|
+
return round;
|
|
142
198
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
199
|
+
const total = ready.length;
|
|
200
|
+
let completed = 0;
|
|
201
|
+
const tick = () => {
|
|
202
|
+
completed += 1;
|
|
203
|
+
ctx.onProgress?.({ current: completed, total });
|
|
148
204
|
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const key = `confirm_${pendingSorted.indexOf(plan.validDest)}`;
|
|
153
|
-
const choice = readAcceptedChoice(ctx.inputResponses, key);
|
|
154
|
-
if (choice === 'skip') {
|
|
155
|
-
return { skipped: plan.pair.destination };
|
|
205
|
+
const { results: execResults, errors: execErrors } = await processInParallel(ready, async (readyPlan) => {
|
|
206
|
+
try {
|
|
207
|
+
return await executeTransfer(op, readyPlan, ctx, pendingSorted);
|
|
156
208
|
}
|
|
157
|
-
|
|
158
|
-
|
|
209
|
+
finally {
|
|
210
|
+
tick();
|
|
159
211
|
}
|
|
212
|
+
}, PARALLEL_CONCURRENCY, ctx.signal);
|
|
213
|
+
for (const { error, index } of execErrors) {
|
|
214
|
+
rethrowIfAborted(error);
|
|
215
|
+
const failed = ready[index];
|
|
216
|
+
if (failed)
|
|
217
|
+
failures.push(pairFailure(failed.pair, error));
|
|
160
218
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
219
|
+
const results = [];
|
|
220
|
+
const skipped = [];
|
|
221
|
+
for (const { value } of execResults) {
|
|
222
|
+
if ('skipped' in value)
|
|
223
|
+
skipped.push(value.skipped);
|
|
224
|
+
else
|
|
225
|
+
results.push(value.value);
|
|
165
226
|
}
|
|
166
|
-
|
|
167
|
-
await ctx.fs.cp(plan.realSource, plan.validDest, {
|
|
168
|
-
recursive: true,
|
|
169
|
-
verbatimSymlinks: true,
|
|
170
|
-
preserveTimestamps: true,
|
|
171
|
-
force: true,
|
|
172
|
-
});
|
|
173
|
-
return { value: { from: plan.realSource, to: plan.validDest } };
|
|
227
|
+
return { results, skipped, failures };
|
|
174
228
|
}
|
|
175
229
|
/**
|
|
176
230
|
* Two-phase move: pre-check every move (no mutation) to build the overwrite
|
|
@@ -180,17 +234,10 @@ async function executeCopy(plan, ctx, pendingSorted, overwrite) {
|
|
|
180
234
|
* an accepted overwrite. Non-pending moves proceed alongside them.
|
|
181
235
|
*/
|
|
182
236
|
async function handleMove(args, ctx) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
execute: (plan, pendingSorted) => executeCopy(plan, ctx, pendingSorted, args.overwrite),
|
|
188
|
-
})
|
|
189
|
-
: await runOverPairs(args.moves, ctx, {
|
|
190
|
-
op: 'move',
|
|
191
|
-
plan: (move) => planMove(move, ctx.fs),
|
|
192
|
-
execute: (plan, pendingSorted) => executeMove(plan, ctx, pendingSorted),
|
|
193
|
-
});
|
|
237
|
+
const op = args.copy ? 'copy' : 'move';
|
|
238
|
+
// `overwrite` is copy-only; move has no confirmation bypass.
|
|
239
|
+
const overwrite = args.copy && args.overwrite;
|
|
240
|
+
const outcome = await runTransfers(args.moves, ctx, op, overwrite);
|
|
194
241
|
if (isInputRequiredResult(outcome))
|
|
195
242
|
return outcome;
|
|
196
243
|
const { results, skipped, failures } = outcome;
|
|
@@ -200,7 +247,7 @@ async function handleMove(args, ctx) {
|
|
|
200
247
|
...(skipped.length > 0 ? { skipped } : {}),
|
|
201
248
|
};
|
|
202
249
|
}
|
|
203
|
-
function buildSummary(verb, results, failures, skipped
|
|
250
|
+
function buildSummary(verb, results, failures, skipped) {
|
|
204
251
|
const successCount = results.length;
|
|
205
252
|
const failCount = failures.length;
|
|
206
253
|
if (failCount === 0 && skipped.length === 0 && successCount === 1) {
|
|
@@ -227,18 +274,19 @@ function buildSummary(verb, results, failures, skipped = []) {
|
|
|
227
274
|
* itself, or moving a symlink would rename the file it points at and leave the
|
|
228
275
|
* link dangling. Every collision check must instead compare the resolved
|
|
229
276
|
* target, or a link moved onto itself (or onto its own target) reads as a real
|
|
230
|
-
* move and renames the link over that target, destroying it.
|
|
277
|
+
* move and renames the link over that target, destroying it. A copy reads
|
|
278
|
+
* through the link, so both views are the resolved one.
|
|
231
279
|
*/
|
|
232
|
-
async function
|
|
280
|
+
async function validateTransferSource(op, source, fs) {
|
|
233
281
|
try {
|
|
234
|
-
const
|
|
235
|
-
const
|
|
236
|
-
return {
|
|
282
|
+
const realSource = await fs.pathGuard.validateExistingPath(source);
|
|
283
|
+
const opSource = op === 'move' ? await fs.pathGuard.validatePathForDelete(source) : realSource;
|
|
284
|
+
return { realSource, opSource };
|
|
237
285
|
}
|
|
238
286
|
catch (error) {
|
|
239
287
|
if (isFsError(error))
|
|
240
288
|
throw error;
|
|
241
|
-
throw new FsError(ErrorCode.ACCESS_DENIED,
|
|
289
|
+
throw new FsError(ErrorCode.ACCESS_DENIED, `${VERB[op]} failed for ${source}`, source);
|
|
242
290
|
}
|
|
243
291
|
}
|
|
244
292
|
async function performRenameWithFallback(validSource, validDest, fsOps, originalSource) {
|
|
@@ -270,7 +318,7 @@ async function performRenameWithFallback(validSource, validDest, fsOps, original
|
|
|
270
318
|
// caller can recover (clean up the duplicate) instead of a silent generic
|
|
271
319
|
// failure that hides the partial completion.
|
|
272
320
|
const baseProblem = Problem.fromUnknown(copyOrRemoveError, ErrorCode.UNKNOWN, originalSource);
|
|
273
|
-
throw new FsError(baseProblem.code, `Cross-device move of ${originalSource}: copy succeeded but source removal failed (destination holds a copy): ${baseProblem.message}`, originalSource,
|
|
321
|
+
throw new FsError(baseProblem.code, `Cross-device move of ${originalSource}: copy succeeded but source removal failed (destination holds a copy): ${baseProblem.message}`, originalSource, copyOrRemoveError);
|
|
274
322
|
}
|
|
275
323
|
throw copyOrRemoveError;
|
|
276
324
|
}
|
|
@@ -317,7 +365,9 @@ export const MOVE = defineTool({
|
|
|
317
365
|
return {
|
|
318
366
|
structured: output,
|
|
319
367
|
text: buildSummary(args.copy ? 'copy' : 'move', output.moves, failures, skipped),
|
|
320
|
-
|
|
368
|
+
// Every requested pair failed: no move, no copy, no user-chosen skip - the
|
|
369
|
+
// call did nothing. A skip is work the caller asked for, so it counts.
|
|
370
|
+
isError: failures.length > 0 && output.moves.length + skipped.length === 0,
|
|
321
371
|
};
|
|
322
372
|
},
|
|
323
373
|
});
|