@j0hanz/filesystem-mcp 2.7.6 → 2.8.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.
Files changed (129) hide show
  1. package/dist/cli.d.ts +1 -9
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/cursor.d.ts +8 -0
  6. package/dist/core/cursor.d.ts.map +1 -1
  7. package/dist/core/cursor.js +14 -0
  8. package/dist/core/cursor.js.map +1 -1
  9. package/dist/core/errors.d.ts +4 -3
  10. package/dist/core/errors.d.ts.map +1 -1
  11. package/dist/core/errors.js +5 -16
  12. package/dist/core/errors.js.map +1 -1
  13. package/dist/core/fs.d.ts +11 -11
  14. package/dist/core/fs.d.ts.map +1 -1
  15. package/dist/core/fs.js +172 -83
  16. package/dist/core/fs.js.map +1 -1
  17. package/dist/core/glob.d.ts +8 -0
  18. package/dist/core/glob.d.ts.map +1 -1
  19. package/dist/core/glob.js +192 -29
  20. package/dist/core/glob.js.map +1 -1
  21. package/dist/core/input-required.d.ts +19 -0
  22. package/dist/core/input-required.d.ts.map +1 -1
  23. package/dist/core/input-required.js +20 -2
  24. package/dist/core/input-required.js.map +1 -1
  25. package/dist/core/observability.d.ts +2 -0
  26. package/dist/core/observability.d.ts.map +1 -1
  27. package/dist/core/observability.js +7 -0
  28. package/dist/core/observability.js.map +1 -1
  29. package/dist/core/path-completer.d.ts.map +1 -1
  30. package/dist/core/path-completer.js +11 -30
  31. package/dist/core/path-completer.js.map +1 -1
  32. package/dist/core/path-utils.d.ts +9 -6
  33. package/dist/core/path-utils.d.ts.map +1 -1
  34. package/dist/core/path-utils.js +13 -19
  35. package/dist/core/path-utils.js.map +1 -1
  36. package/dist/core/path.d.ts +1 -20
  37. package/dist/core/path.d.ts.map +1 -1
  38. package/dist/core/path.js +17 -69
  39. package/dist/core/path.js.map +1 -1
  40. package/dist/core/read.d.ts +7 -3
  41. package/dist/core/read.d.ts.map +1 -1
  42. package/dist/core/read.js +32 -50
  43. package/dist/core/read.js.map +1 -1
  44. package/dist/core/schema.d.ts +6 -1
  45. package/dist/core/schema.d.ts.map +1 -1
  46. package/dist/core/schema.js +17 -3
  47. package/dist/core/schema.js.map +1 -1
  48. package/dist/core/search.d.ts.map +1 -1
  49. package/dist/core/search.js +104 -51
  50. package/dist/core/search.js.map +1 -1
  51. package/dist/core/sensitive.d.ts.map +1 -1
  52. package/dist/core/sensitive.js +7 -14
  53. package/dist/core/sensitive.js.map +1 -1
  54. package/dist/core/store.d.ts +36 -1
  55. package/dist/core/store.d.ts.map +1 -1
  56. package/dist/core/store.js +71 -6
  57. package/dist/core/store.js.map +1 -1
  58. package/dist/core/util.d.ts +40 -5
  59. package/dist/core/util.d.ts.map +1 -1
  60. package/dist/core/util.js +75 -5
  61. package/dist/core/util.js.map +1 -1
  62. package/dist/index.js +5 -16
  63. package/dist/index.js.map +1 -1
  64. package/dist/prompts.d.ts +1 -1
  65. package/dist/prompts.d.ts.map +1 -1
  66. package/dist/prompts.js +1 -1
  67. package/dist/prompts.js.map +1 -1
  68. package/dist/resources.d.ts +6 -3
  69. package/dist/resources.d.ts.map +1 -1
  70. package/dist/resources.js +46 -25
  71. package/dist/resources.js.map +1 -1
  72. package/dist/server.d.ts +9 -6
  73. package/dist/server.d.ts.map +1 -1
  74. package/dist/server.js +8 -9
  75. package/dist/server.js.map +1 -1
  76. package/dist/tools/create.d.ts.map +1 -1
  77. package/dist/tools/create.js +13 -25
  78. package/dist/tools/create.js.map +1 -1
  79. package/dist/tools/define.d.ts +8 -7
  80. package/dist/tools/define.d.ts.map +1 -1
  81. package/dist/tools/define.js +48 -16
  82. package/dist/tools/define.js.map +1 -1
  83. package/dist/tools/delete.d.ts.map +1 -1
  84. package/dist/tools/delete.js +46 -27
  85. package/dist/tools/delete.js.map +1 -1
  86. package/dist/tools/edit.d.ts.map +1 -1
  87. package/dist/tools/edit.js +24 -39
  88. package/dist/tools/edit.js.map +1 -1
  89. package/dist/tools/find-files.d.ts.map +1 -1
  90. package/dist/tools/find-files.js +45 -62
  91. package/dist/tools/find-files.js.map +1 -1
  92. package/dist/tools/index.d.ts +3 -12
  93. package/dist/tools/index.d.ts.map +1 -1
  94. package/dist/tools/index.js +2 -10
  95. package/dist/tools/index.js.map +1 -1
  96. package/dist/tools/list.d.ts.map +1 -1
  97. package/dist/tools/list.js +81 -74
  98. package/dist/tools/list.js.map +1 -1
  99. package/dist/tools/move.d.ts.map +1 -1
  100. package/dist/tools/move.js +17 -14
  101. package/dist/tools/move.js.map +1 -1
  102. package/dist/tools/patch.d.ts.map +1 -1
  103. package/dist/tools/patch.js +3 -6
  104. package/dist/tools/patch.js.map +1 -1
  105. package/dist/tools/read.d.ts.map +1 -1
  106. package/dist/tools/read.js +31 -40
  107. package/dist/tools/read.js.map +1 -1
  108. package/dist/tools/replace-text.d.ts.map +1 -1
  109. package/dist/tools/replace-text.js +24 -33
  110. package/dist/tools/replace-text.js.map +1 -1
  111. package/dist/tools/search-text.d.ts.map +1 -1
  112. package/dist/tools/search-text.js +42 -69
  113. package/dist/tools/search-text.js.map +1 -1
  114. package/dist/transport/http-policy.d.ts +3 -2
  115. package/dist/transport/http-policy.d.ts.map +1 -1
  116. package/dist/transport/http-policy.js +3 -2
  117. package/dist/transport/http-policy.js.map +1 -1
  118. package/dist/transport/http.d.ts.map +1 -1
  119. package/dist/transport/http.js +2 -3
  120. package/dist/transport/http.js.map +1 -1
  121. package/dist/transport/shared.d.ts +7 -5
  122. package/dist/transport/shared.d.ts.map +1 -1
  123. package/dist/transport/shared.js +8 -2
  124. package/dist/transport/shared.js.map +1 -1
  125. package/dist/transport/stdio.d.ts +4 -1
  126. package/dist/transport/stdio.d.ts.map +1 -1
  127. package/dist/transport/stdio.js +45 -12
  128. package/dist/transport/stdio.js.map +1 -1
  129. package/package.json +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"find-files.js","sourceRoot":"","sources":["../../src/tools/find-files.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { paginate } from '../core/cursor.ts';\nimport { formatCount, pageTrailer, truncateProgressPattern } from '../core/fmt.ts';\nimport { toPosixRelative } from '../core/path.ts';\nimport {\n CursorSchema,\n IncludeHidden,\n IncludeIgnored,\n MaxDepth,\n OptionalPath,\n SafeGlobPattern,\n} from '../core/schema.ts';\nimport { searchFiles } from '../core/search.ts';\nimport type { JsonResourceResult } from '../core/store.ts';\nimport { putJsonResource } from '../core/store.ts';\nimport {\n DEFAULT_SEARCH_RESULTS,\n DEFAULT_SEARCH_TIMEOUT_MS,\n MAX_SEARCH_RESULTS,\n} from '../core/util.ts';\nimport { defineTool, type ToolCtx } from './define.ts';\n\n// ---------------------------------------------------------------------------\n\nconst SearchFilesInputSchema = z.strictObject({\n path: OptionalPath.describe('Base directory to search under (default: first allowed root)'),\n pattern: SafeGlobPattern.describe('Glob pattern to match file paths (e.g. **/*.ts, src/**/*.js)'),\n maxResults: z\n .uint32()\n .min(1)\n .max(MAX_SEARCH_RESULTS)\n .optional()\n .default(DEFAULT_SEARCH_RESULTS)\n .describe('Maximum number of matching files to return per page'),\n includeIgnored: IncludeIgnored,\n includeHidden: IncludeHidden,\n sortBy: z\n .enum(['name', 'path'])\n .optional()\n .default('path')\n .describe('Sort order: path = full path (default), name = basename only'),\n maxDepth: MaxDepth,\n cursor: CursorSchema,\n});\n\ninterface SearchFileResult {\n path: string;\n}\ninterface SearchFilesOutput {\n root: string;\n results: SearchFileResult[];\n totalMatches?: number;\n filesScanned?: number;\n skippedInaccessible?: number;\n stoppedReason?: 'maxResults' | 'timeout';\n resourceUri?: string;\n nextCursor?: string;\n}\n\n/** The engine's own summary plus its root is the page metadata; nothing is copied out of it. */\ntype SearchFilesPageMetadata = Awaited<ReturnType<typeof searchFiles>>['summary'] & {\n readonly root: string;\n};\n\nfunction searchFilesOutput(\n results: readonly SearchFileResult[],\n metadata: SearchFilesPageMetadata,\n nextCursor: string | undefined,\n resourceUri: string | undefined,\n): SearchFilesOutput {\n return {\n root: metadata.root,\n results: [...results],\n totalMatches: metadata.matched,\n filesScanned: metadata.filesScanned,\n ...(metadata.skippedInaccessible ? { skippedInaccessible: metadata.skippedInaccessible } : {}),\n ...(metadata.stoppedReason !== undefined ? { stoppedReason: metadata.stoppedReason } : {}),\n ...(resourceUri !== undefined ? { resourceUri } : {}),\n ...(nextCursor !== undefined ? { nextCursor } : {}),\n };\n}\n\nasync function handleSearchFiles(\n args: z.infer<typeof SearchFilesInputSchema>,\n ctx: ToolCtx,\n): Promise<{\n structured: SearchFilesOutput;\n offset: number;\n total: number;\n link?: ReturnType<typeof putJsonResource>['link'];\n}> {\n const requestedBasePath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n const queryKey = JSON.stringify({\n method: 'find_files',\n path: requestedBasePath,\n pattern: args.pattern,\n includeIgnored: args.includeIgnored,\n includeHidden: args.includeHidden,\n sortBy: args.sortBy,\n maxDepth: args.maxDepth,\n });\n const paged = await paginate<SearchFileResult, SearchFilesPageMetadata, JsonResourceResult>({\n store: ctx.pageStore,\n queryKey,\n cursor: args.cursor,\n pageSize: args.maxResults,\n produce: async () => {\n const basePath = await ctx.fs.pathGuard.validateExistingDirectory(requestedBasePath);\n const searchOptions: Parameters<typeof searchFiles>[2] = {\n maxResults: MAX_SEARCH_RESULTS,\n includeHidden: args.includeHidden,\n sortBy: args.sortBy,\n skipIgnored: !args.includeIgnored,\n ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),\n signal: ctx.signal,\n };\n const result = await searchFiles(basePath, args.pattern, searchOptions, ctx.fs.pathGuard);\n return {\n items: result.results.map((r) => ({ path: toPosixRelative(result.basePath, r.path) })),\n metadata: { root: result.basePath, ...result.summary },\n truncated: result.summary.truncated,\n };\n },\n externalize: (results) => putJsonResource(ctx.resourceStore, `${args.pattern} files`, results),\n });\n\n return {\n structured: searchFilesOutput(\n paged.page,\n paged.metadata,\n paged.nextCursor,\n paged.resource?.entry.uri,\n ),\n offset: paged.offset,\n total: paged.metadata.matched,\n ...(paged.resource ? { link: paged.resource.link } : {}),\n };\n}\n\nexport const FIND_FILES = defineTool<typeof SearchFilesInputSchema, SearchFilesOutput>({\n name: 'find_files',\n title: 'Find Files',\n description:\n 'Find files by name or glob pattern; returns file paths relative to the searched directory. ' +\n 'A pattern without / matches file names at any depth. search_text searches contents.',\n input: SearchFilesInputSchema,\n readOnlyHint: true,\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n progress: (args) => ({\n label: 'Find',\n subject: truncateProgressPattern(args.pattern),\n }),\n progressDone: (_args, result) => ({\n detail: formatCount(result.totalMatches ?? 0, 'match', 'matches'),\n }),\n accessPaths: (args) => (args.path ? [args.path] : []),\n run: async (args, ctx) => {\n const { structured, offset, total, link } = await handleSearchFiles(args, ctx);\n const body =\n structured.results.length > 0\n ? structured.results.map((r) => r.path).join('\\n')\n : `No files matching '${args.pattern}'`;\n const text =\n body +\n pageTrailer({\n offset,\n shown: structured.results.length,\n total,\n noun: 'files',\n tool: 'find_files',\n nextCursor: structured.nextCursor,\n nextArgs: args,\n stoppedReason: structured.stoppedReason,\n skippedInaccessible: structured.skippedInaccessible,\n });\n if (link) {\n return { structured, text, resources: [link] };\n }\n return { structured, text };\n },\n});\n"]}
1
+ {"version":3,"file":"find-files.js","sourceRoot":"","sources":["../../src/tools/find-files.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { pageQueryKey, paginate } from '../core/cursor.ts';\nimport { formatCount, pageTrailer, truncateProgressPattern } from '../core/fmt.ts';\nimport { toPosixRelative } from '../core/path-utils.ts';\nimport {\n CursorSchema,\n IncludeHidden,\n IncludeIgnored,\n MaxDepth,\n OptionalPath,\n SafeGlobPattern,\n} from '../core/schema.ts';\nimport { searchFiles } from '../core/search.ts';\nimport type { JsonResourceResult } from '../core/store.ts';\nimport { putJsonResource } from '../core/store.ts';\nimport {\n DEFAULT_SEARCH_RESULTS,\n DEFAULT_SEARCH_TIMEOUT_MS,\n MAX_SEARCH_RESULTS,\n} from '../core/util.ts';\nimport { defineTool } from './define.ts';\n\n// ---------------------------------------------------------------------------\n\nconst SearchFilesInputSchema = z.strictObject({\n path: OptionalPath.describe('Base directory to search under (default: first allowed root)'),\n pattern: SafeGlobPattern.describe('Glob pattern to match file paths (e.g. **/*.ts, src/**/*.js)'),\n maxResults: z\n .uint32()\n .min(1)\n .max(MAX_SEARCH_RESULTS)\n .optional()\n .default(DEFAULT_SEARCH_RESULTS)\n .describe('Maximum number of matching files to return per page'),\n includeIgnored: IncludeIgnored,\n includeHidden: IncludeHidden,\n sortBy: z\n .enum(['name', 'path'])\n .optional()\n .default('path')\n .describe('Sort order: path = full path (default), name = basename only'),\n maxDepth: MaxDepth,\n cursor: CursorSchema,\n});\n\ninterface SearchFileResult {\n path: string;\n}\ninterface SearchFilesOutput {\n root: string;\n results: SearchFileResult[];\n totalMatches?: number;\n filesScanned?: number;\n skippedInaccessible?: number;\n stoppedReason?: 'maxResults' | 'timeout';\n resourceUri?: string;\n nextCursor?: string;\n}\n\n/** The engine's own summary plus its root is the page metadata; nothing is copied out of it. */\ntype SearchFilesPageMetadata = Awaited<ReturnType<typeof searchFiles>>['summary'] & {\n readonly root: string;\n};\n\nexport const FIND_FILES = defineTool<typeof SearchFilesInputSchema, SearchFilesOutput>({\n name: 'find_files',\n title: 'Find Files',\n description:\n 'Find files by name or glob pattern; returns file paths relative to the searched directory. ' +\n 'A pattern without / matches file names at any depth. search_text searches contents.',\n input: SearchFilesInputSchema,\n readOnlyHint: true,\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n progress: (args) => ({\n label: 'Find',\n subject: truncateProgressPattern(args.pattern),\n }),\n progressDone: (_args, result) => ({\n detail: formatCount(result.totalMatches ?? 0, 'match', 'matches'),\n }),\n accessPaths: (args) => (args.path ? [args.path] : []),\n run: async (args, ctx) => {\n const requestedBasePath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n const paged = await paginate<SearchFileResult, SearchFilesPageMetadata, JsonResourceResult>({\n store: ctx.pageStore,\n queryKey: pageQueryKey('find_files', { ...args, path: requestedBasePath }, [\n 'cursor',\n 'maxResults',\n ]),\n cursor: args.cursor,\n pageSize: args.maxResults,\n produce: async () => {\n const basePath = await ctx.fs.pathGuard.validateExistingDirectory(requestedBasePath);\n const searchOptions: Parameters<typeof searchFiles>[2] = {\n maxResults: MAX_SEARCH_RESULTS,\n includeHidden: args.includeHidden,\n sortBy: args.sortBy,\n skipIgnored: !args.includeIgnored,\n ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),\n signal: ctx.signal,\n };\n const result = await searchFiles(basePath, args.pattern, searchOptions, ctx.fs.pathGuard);\n return {\n items: result.results.map((r) => ({ path: toPosixRelative(result.basePath, r.path) })),\n metadata: { root: result.basePath, ...result.summary },\n truncated: result.summary.truncated,\n };\n },\n externalize: (results) =>\n putJsonResource(ctx.resourceStore, `${args.pattern} files`, results),\n });\n\n const { metadata, offset } = paged;\n const structured: SearchFilesOutput = {\n root: metadata.root,\n results: [...paged.page],\n totalMatches: metadata.matched,\n filesScanned: metadata.filesScanned,\n ...(metadata.skippedInaccessible\n ? { skippedInaccessible: metadata.skippedInaccessible }\n : {}),\n ...(metadata.stoppedReason !== undefined ? { stoppedReason: metadata.stoppedReason } : {}),\n ...(paged.resource ? { resourceUri: paged.resource.entry.uri } : {}),\n ...(paged.nextCursor !== undefined ? { nextCursor: paged.nextCursor } : {}),\n };\n const body =\n structured.results.length > 0\n ? structured.results.map((r) => r.path).join('\\n')\n : `No files matching '${args.pattern}'`;\n const text =\n body +\n pageTrailer({\n offset,\n shown: structured.results.length,\n total: metadata.matched,\n noun: 'files',\n tool: 'find_files',\n nextCursor: structured.nextCursor,\n nextArgs: args,\n stoppedReason: structured.stoppedReason,\n skippedInaccessible: structured.skippedInaccessible,\n });\n return { structured, text, resources: paged.resource ? [paged.resource.link] : [] };\n },\n});\n"]}
@@ -1,7 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import type { PathGuard } from '../core/path.ts';
3
- import type { PageSnapshotStore, ResourceStore } from '../core/store.ts';
4
- import type { DefinedTool } from './define.ts';
1
+ import type { DefinedTool, ToolDeps } from './define.ts';
5
2
  import { FIND_FILES } from './find-files.ts';
6
3
  import { LIST_ROOTS } from './list-roots.ts';
7
4
  import { LIST } from './list.ts';
@@ -13,14 +10,8 @@ export declare const MUTATING_TOOL_NAMES: Set<string>;
13
10
  /** The tools a server registers at this setting — the one owner of the `--read-only` gate. */
14
11
  export declare function registeredTools(readOnly: boolean): readonly DefinedTool[];
15
12
  export { LIST, LIST_ROOTS, READ, SEARCH_TEXT, FIND_FILES, STAT };
16
- interface ToolRegistrarDeps {
17
- readonly server: McpServer;
18
- readonly pathGuard: PathGuard;
19
- readonly pageStore: PageSnapshotStore;
20
- readonly resourceStore: ResourceStore;
21
- readonly readOnly?: boolean;
22
- readonly era?: 'legacy' | 'modern';
23
- readonly transport?: 'stdio' | 'http';
13
+ interface ToolRegistrarDeps extends ToolDeps {
14
+ readonly readOnly: boolean;
24
15
  }
25
16
  export declare function registerTools(deps: ToolRegistrarDeps): void;
26
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,eAAO,MAAM,SAAS,oLAcZ,CAAC;AAEX,eAAO,MAAM,mBAAmB,aAE/B,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,WAAW,EAAE,CAEzE;AAOD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAEjE,UAAU,iBAAiB;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAY3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,eAAO,MAAM,SAAS,oLAcZ,CAAC;AAEX,eAAO,MAAM,mBAAmB,aAE/B,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,WAAW,EAAE,CAEzE;AAOD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAEjE,UAAU,iBAAkB,SAAQ,QAAQ;IAC1C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAI3D"}
@@ -38,16 +38,8 @@ export function registeredTools(readOnly) {
38
38
  // MUTATING_TOOL_NAMES and ALL_TOOLS instead.
39
39
  export { LIST, LIST_ROOTS, READ, SEARCH_TEXT, FIND_FILES, STAT };
40
40
  export function registerTools(deps) {
41
- const toolDeps = {
42
- server: deps.server,
43
- pathGuard: deps.pathGuard,
44
- pageStore: deps.pageStore,
45
- resourceStore: deps.resourceStore,
46
- ...(deps.era ? { era: deps.era } : {}),
47
- ...(deps.transport ? { transport: deps.transport } : {}),
48
- };
49
- for (const tool of registeredTools(deps.readOnly ?? false)) {
50
- tool.register(toolDeps);
41
+ for (const tool of registeredTools(deps.readOnly)) {
42
+ tool.register(deps);
51
43
  }
52
44
  }
53
45
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,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,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM;IACN,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,YAAY;IACZ,UAAU;IACV,WAAW;IACX,UAAU;IACV,IAAI;CACI,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACxC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAC5D,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,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAYjE,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;QACjC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzD,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 { PathGuard } from '../core/path.ts';\nimport type { PageSnapshotStore, ResourceStore } from '../core/store.ts';\nimport { CREATE } from './create.ts';\nimport type { DefinedTool } from './define.ts';\nimport { DELETE } from './delete.ts';\nimport { DIFF } from './diff.ts';\nimport { EDIT } from './edit.ts';\nimport { FIND_FILES } from './find-files.ts';\nimport { LIST_ROOTS } from './list-roots.ts';\nimport { LIST } from './list.ts';\nimport { MOVE } from './move.ts';\nimport { PATCH } from './patch.ts';\nimport { READ } from './read.ts';\nimport { REPLACE_TEXT } from './replace-text.ts';\nimport { SEARCH_TEXT } from './search-text.ts';\nimport { STAT } from './stat.ts';\n\nexport const ALL_TOOLS = [\n CREATE,\n DELETE,\n DIFF,\n EDIT,\n LIST,\n MOVE,\n PATCH,\n READ,\n REPLACE_TEXT,\n LIST_ROOTS,\n SEARCH_TEXT,\n FIND_FILES,\n STAT,\n] as const;\n\nexport const MUTATING_TOOL_NAMES = new Set(\n ALL_TOOLS.filter((t) => !t.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_ROOTS, READ, SEARCH_TEXT, FIND_FILES, STAT };\n\ninterface ToolRegistrarDeps {\n readonly server: McpServer;\n readonly pathGuard: PathGuard;\n readonly pageStore: PageSnapshotStore;\n readonly resourceStore: ResourceStore;\n readonly readOnly?: boolean;\n readonly era?: 'legacy' | 'modern';\n readonly transport?: 'stdio' | 'http';\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 ...(deps.era ? { era: deps.era } : {}),\n ...(deps.transport ? { transport: deps.transport } : {}),\n };\n for (const tool of registeredTools(deps.readOnly ?? false)) {\n tool.register(toolDeps);\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,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,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM;IACN,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,YAAY;IACZ,UAAU;IACV,WAAW;IACX,UAAU;IACV,IAAI;CACI,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACxC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAC5D,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,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAMjE,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,KAAK,MAAM,IAAI,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;AACH,CAAC","sourcesContent":["import { CREATE } from './create.ts';\nimport type { DefinedTool, ToolDeps } from './define.ts';\nimport { DELETE } from './delete.ts';\nimport { DIFF } from './diff.ts';\nimport { EDIT } from './edit.ts';\nimport { FIND_FILES } from './find-files.ts';\nimport { LIST_ROOTS } from './list-roots.ts';\nimport { LIST } from './list.ts';\nimport { MOVE } from './move.ts';\nimport { PATCH } from './patch.ts';\nimport { READ } from './read.ts';\nimport { REPLACE_TEXT } from './replace-text.ts';\nimport { SEARCH_TEXT } from './search-text.ts';\nimport { STAT } from './stat.ts';\n\nexport const ALL_TOOLS = [\n CREATE,\n DELETE,\n DIFF,\n EDIT,\n LIST,\n MOVE,\n PATCH,\n READ,\n REPLACE_TEXT,\n LIST_ROOTS,\n SEARCH_TEXT,\n FIND_FILES,\n STAT,\n] as const;\n\nexport const MUTATING_TOOL_NAMES = new Set(\n ALL_TOOLS.filter((t) => !t.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_ROOTS, READ, SEARCH_TEXT, FIND_FILES, STAT };\n\ninterface ToolRegistrarDeps extends ToolDeps {\n readonly readOnly: boolean;\n}\n\nexport function registerTools(deps: ToolRegistrarDeps): void {\n for (const tool of registeredTools(deps.readOnly)) {\n tool.register(deps);\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AA8SA,eAAO,MAAM,IAAI,mCAsCf,CAAC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AAuPA,eAAO,MAAM,IAAI,mCAuFf,CAAC"}
@@ -1,11 +1,10 @@
1
1
  import { basename, posix } from 'node:path';
2
2
  import * as z from 'zod/v4';
3
- import { paginate } from "../core/cursor.js";
4
- import { ErrorCode } from "../core/errors.js";
3
+ import { pageQueryKey, paginate } from "../core/cursor.js";
4
+ import { ErrorCode, isFsError, isSkippableErrno, SKIPPABLE_FS_CODES } from "../core/errors.js";
5
5
  import { pageTrailer } from "../core/fmt.js";
6
6
  import { globEntries } from "../core/glob.js";
7
- import { resolveEntryType } from "../core/path-utils.js";
8
- import { toPosixRelative } from "../core/path.js";
7
+ import { resolveEntryType, toPosixRelative } from "../core/path-utils.js";
9
8
  import { CursorSchema, IncludeHidden, IncludeIgnored, OptionalPath, PositiveInt, } from "../core/schema.js";
10
9
  import { putJsonResource } from "../core/store.js";
11
10
  import { DEFAULT_SEARCH_TIMEOUT_MS, DEFAULT_TREE_ENTRIES, MAX_LIST_ENTRIES, MAX_TREE_DEPTH, } from "../core/util.js";
@@ -29,6 +28,31 @@ function compareEntries(a, b) {
29
28
  return rankDiff;
30
29
  return a.name.localeCompare(b.name);
31
30
  }
31
+ /**
32
+ * True when a walked entry is inside the allowed directories and not sensitive,
33
+ * on both its requested and resolved paths. Every entry is realpath-resolved: a
34
+ * symlinked ancestor pointing outside the sandbox would otherwise pass the
35
+ * lexical check (fs.glob follows symlinks and yields external entries as
36
+ * non-symlink dirents). Escapes and sensitive targets throw ACCESS_DENIED and
37
+ * skippable errors filter the entry; anything else is fatal. The accepted
38
+ * TOCTOU window is documented on PathGuard.
39
+ */
40
+ async function isEntryAccessible(guard, entryPath) {
41
+ try {
42
+ const { requestedPath, resolvedPath } = await guard.validateExistingPathDetailed(entryPath);
43
+ return !guard.isSensitive(requestedPath) && !guard.isSensitive(resolvedPath);
44
+ }
45
+ catch (error) {
46
+ if (isFsError(error)) {
47
+ if (SKIPPABLE_FS_CODES.has(error.code))
48
+ return false;
49
+ throw error;
50
+ }
51
+ if (isSkippableErrno(error))
52
+ return false;
53
+ throw error;
54
+ }
55
+ }
32
56
  async function collect(rootPath, options) {
33
57
  const entries = [];
34
58
  let scanned = 0;
@@ -56,7 +80,7 @@ async function collect(rootPath, options) {
56
80
  const entryType = resolveEntryType(entry.dirent);
57
81
  const relPath = toPosixRelative(rootPath, entry.path);
58
82
  const name = basename(relPath);
59
- const accessible = await options.pathGuard.isEntryAccessible(entry.path);
83
+ const accessible = await isEntryAccessible(options.pathGuard, entry.path);
60
84
  if (!accessible)
61
85
  continue;
62
86
  totalEntries++;
@@ -147,71 +171,6 @@ const ListInputSchema = z.strictObject({
147
171
  includeIgnored: IncludeIgnored,
148
172
  cursor: CursorSchema,
149
173
  });
150
- async function handleList(args, ctx) {
151
- const path = args.path;
152
- const resolvedPath = ctx.fs.pathGuard.resolvePathOrRoot(path);
153
- const queryKey = JSON.stringify({
154
- method: 'list',
155
- path: resolvedPath,
156
- maxDepth: args.maxDepth,
157
- includeHidden: args.includeHidden,
158
- includeIgnored: args.includeIgnored,
159
- });
160
- const paged = await paginate({
161
- store: ctx.pageStore,
162
- queryKey,
163
- cursor: args.cursor,
164
- pageSize: args.maxEntries,
165
- produce: async () => {
166
- const validDir = await ctx.fs.pathGuard.validateExistingDirectory(resolvedPath);
167
- const result = await collect(validDir, {
168
- maxDepth: args.maxDepth,
169
- includeHidden: args.includeHidden,
170
- includeIgnored: args.includeIgnored,
171
- signal: ctx.signal,
172
- pathGuard: ctx.fs.pathGuard,
173
- entryCap: MAX_LIST_ENTRIES,
174
- onProgress: ctx.onProgress,
175
- });
176
- return {
177
- items: result.entries,
178
- metadata: {
179
- path: validDir,
180
- totalEntries: result.totalEntries,
181
- totalFiles: result.totalFiles,
182
- totalDirectories: result.totalDirectories,
183
- },
184
- truncated: result.totalEntries > result.entries.length,
185
- };
186
- },
187
- externalize: (entries, metadata) => {
188
- const name = basename(metadata.path);
189
- // The stored tree is the whole collected set, bounded by the hard cap.
190
- const fullOutput = {
191
- entries,
192
- markdown: renderMarkdown(name, entries),
193
- totalEntries: metadata.totalEntries,
194
- totalFiles: metadata.totalFiles,
195
- totalDirectories: metadata.totalDirectories,
196
- ...(metadata.totalEntries > entries.length ? { truncated: true } : {}),
197
- };
198
- return putJsonResource(ctx.resourceStore, `${name} tree`, fullOutput);
199
- },
200
- });
201
- const resourceUri = paged.resource?.entry.uri;
202
- return {
203
- structured: {
204
- ...paged.metadata,
205
- entries: [...paged.page],
206
- entryCount: paged.page.length,
207
- ...(resourceUri !== undefined ? { resourceUri } : {}),
208
- ...(paged.nextCursor !== undefined ? { nextCursor: paged.nextCursor } : {}),
209
- },
210
- markdown: renderMarkdown(basename(paged.metadata.path), [...paged.page]),
211
- offset: paged.offset,
212
- ...(paged.resource ? { link: paged.resource.link } : {}),
213
- };
214
- }
215
174
  export const LIST = defineTool({
216
175
  name: 'list',
217
176
  title: 'List',
@@ -227,13 +186,61 @@ export const LIST = defineTool({
227
186
  }),
228
187
  accessPaths: (args) => (args.path ? [args.path] : []),
229
188
  run: async (args, ctx) => {
230
- const { structured, markdown, offset, link } = await handleList(args, ctx);
189
+ const resolvedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);
190
+ const paged = await paginate({
191
+ store: ctx.pageStore,
192
+ queryKey: pageQueryKey('list', { ...args, path: resolvedPath }, ['cursor', 'maxEntries']),
193
+ cursor: args.cursor,
194
+ pageSize: args.maxEntries,
195
+ produce: async () => {
196
+ const validDir = await ctx.fs.pathGuard.validateExistingDirectory(resolvedPath);
197
+ const result = await collect(validDir, {
198
+ maxDepth: args.maxDepth,
199
+ includeHidden: args.includeHidden,
200
+ includeIgnored: args.includeIgnored,
201
+ signal: ctx.signal,
202
+ pathGuard: ctx.fs.pathGuard,
203
+ entryCap: MAX_LIST_ENTRIES,
204
+ onProgress: ctx.onProgress,
205
+ });
206
+ return {
207
+ items: result.entries,
208
+ metadata: {
209
+ path: validDir,
210
+ totalEntries: result.totalEntries,
211
+ totalFiles: result.totalFiles,
212
+ totalDirectories: result.totalDirectories,
213
+ },
214
+ truncated: result.totalEntries > result.entries.length,
215
+ };
216
+ },
217
+ externalize: (entries, metadata) => {
218
+ const name = basename(metadata.path);
219
+ // The stored tree is the whole collected set, bounded by the hard cap.
220
+ const fullOutput = {
221
+ entries,
222
+ markdown: renderMarkdown(name, entries),
223
+ totalEntries: metadata.totalEntries,
224
+ totalFiles: metadata.totalFiles,
225
+ totalDirectories: metadata.totalDirectories,
226
+ ...(metadata.totalEntries > entries.length ? { truncated: true } : {}),
227
+ };
228
+ return putJsonResource(ctx.resourceStore, `${name} tree`, fullOutput);
229
+ },
230
+ });
231
+ const structured = {
232
+ ...paged.metadata,
233
+ entries: [...paged.page],
234
+ entryCount: paged.page.length,
235
+ ...(paged.resource ? { resourceUri: paged.resource.entry.uri } : {}),
236
+ ...(paged.nextCursor !== undefined ? { nextCursor: paged.nextCursor } : {}),
237
+ };
231
238
  // The tree is what the model reads, so position and the full-list URI ride
232
239
  // the text too — paging needs no second lookup. `pageTrailer` is the one
233
240
  // owner of that line, and it owes it on the last page as much as the first.
234
- const text = markdown +
241
+ const text = renderMarkdown(basename(paged.metadata.path), [...paged.page]) +
235
242
  pageTrailer({
236
- offset,
243
+ offset: paged.offset,
237
244
  shown: structured.entryCount,
238
245
  total: structured.totalEntries,
239
246
  noun: 'entries',
@@ -245,7 +252,7 @@ export const LIST = defineTool({
245
252
  return {
246
253
  structured,
247
254
  text,
248
- ...(link ? { resources: [link] } : {}),
255
+ ...(paged.resource ? { resources: [paged.resource.link] } : {}),
249
256
  };
250
257
  },
251
258
  });
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAkB,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AA0BvD,SAAS,aAAa,CAAC,IAAe;IACpC,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,cAAc,CAAC,CAAiB,EAAE,CAAiB;IAC1D,2DAA2D;IAC3D,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAErE,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAE/D,oDAAoD;IACpD,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACpC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,QAAgB,EAAE,OAAuB;IAC9D,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAElE,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,WAAW,CAAC;QACpC,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,WAAW,EAAE,CAAC,OAAO,CAAC,cAAc;QACpC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,aAAa,EAAE,KAAK;QACpB,uEAAuE;QACvE,+EAA+E;QAC/E,0EAA0E;QAC1E,QAAQ,EAAE,OAAO,CAAC,QAAQ,GAAG,CAAC;QAC9B,SAAS,EAAE,KAAK;KACjB,CAAC,EAAE,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAChC,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAEzC,MAAM,SAAS,GAAc,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE/B,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,YAAY,EAAE,CAAC;QACf,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;YAC9B,gBAAgB,EAAE,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,UAAU,EAAE,CAAC;QACf,CAAC;QAED,MAAM,cAAc,GAAmB;YACrC,IAAI;YACJ,YAAY,EAAE,OAAO;YACrB,IAAI,EAAE,SAAS;SAChB,CAAC;QAEF,IAAI,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAChC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAE7B,OAAO;QACL,OAAO;QACP,YAAY;QACZ,UAAU;QACV,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED,MAAM,GAAG,GAAG,MAAM,CAAC;AACnB,MAAM,KAAK,GAAG,MAAM,CAAC;AACrB,MAAM,IAAI,GAAG,MAAM,CAAC;AACpB,MAAM,MAAM,GAAG,MAAM,CAAC;AAEtB,SAAS,cAAc,CAAC,QAAgB,EAAE,IAA+B;IACvE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEvC,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,KACE,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,EAC3C,GAAG,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAChC,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EACxB,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAE7B,+BAA+B;IAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,EAA4B,CAAC;IACvD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,QAAQ;YAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,KAAK,GAAa,CAAC,QAAQ,CAAC,CAAC;IAEnC,SAAS,cAAc,CAAC,SAAiB,EAAE,MAAc;QACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,MAAM,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YACzC,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAEtF,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC;YACpC,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACrD,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,cAAc,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B,MAAM,eAAe,GAAG,CAAC,CAAC,YAAY,CAAC;IACrC,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,iDAAiD,CAAC;IAC9E,QAAQ,EAAE,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC;SACtC,OAAO,CAAC,kBAAkB,CAAC;SAC3B,QAAQ,CACP,6CAA6C,MAAM,CAAC,kBAAkB,CAAC,gDAAgD,CACxH;IACH,UAAU,EAAE,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC;SAC1C,OAAO,CAAC,oBAAoB,CAAC;SAC7B,QAAQ,CACP,uBAAuB,MAAM,CAAC,oBAAoB,CAAC,oGAAoG,CACxJ;IACH,aAAa,EAAE,aAAa;IAC5B,cAAc,EAAE,cAAc;IAC9B,MAAM,EAAE,YAAY;CACrB,CAAC,CAAC;AAgBH,KAAK,UAAU,UAAU,CACvB,IAAqC,EACrC,GAAY;IAOZ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,MAAM,YAAY,GAAG,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAuD;QACjF,KAAK,EAAE,GAAG,CAAC,SAAS;QACpB,QAAQ;QACR,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ,EAAE,IAAI,CAAC,UAAU;QACzB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,yBAAyB,CAAC,YAAY,CAAC,CAAC;YAChF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE;gBACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,cAAc,EAAE,IAAI,CAAC,cAAc;gBACnC,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS;gBAC3B,QAAQ,EAAE,gBAAgB;gBAC1B,UAAU,EAAE,GAAG,CAAC,UAAU;aAC3B,CAAC,CAAC;YACH,OAAO;gBACL,KAAK,EAAE,MAAM,CAAC,OAAO;gBACrB,QAAQ,EAAE;oBACR,IAAI,EAAE,QAAQ;oBACd,YAAY,EAAE,MAAM,CAAC,YAAY;oBACjC,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;iBAC1C;gBACD,SAAS,EAAE,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM;aACvD,CAAC;QACJ,CAAC;QACD,WAAW,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACrC,uEAAuE;YACvE,MAAM,UAAU,GAAG;gBACjB,OAAO;gBACP,QAAQ,EAAE,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC;gBACvC,YAAY,EAAE,QAAQ,CAAC,YAAY;gBACnC,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;gBAC3C,GAAG,CAAC,QAAQ,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACvE,CAAC;YACF,OAAO,eAAe,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,IAAI,OAAO,EAAE,UAAU,CAAC,CAAC;QACxE,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC;IAC9C,OAAO;QACL,UAAU,EAAE;YACV,GAAG,KAAK,CAAC,QAAQ;YACjB,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YACxB,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM;YAC7B,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5E;QACD,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QACxE,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzD,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAqC;IACjE,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,WAAW,EACT,mFAAmF;QACnF,+BAA+B;IACjC,KAAK,EAAE,eAAe;IACtB,YAAY,EAAE,IAAI;IAClB,SAAS,EAAE,yBAAyB;IACpC,gBAAgB,EAAE,SAAS,CAAC,aAAa;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACnB,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG;KAC/C,CAAC;IACF,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC3E,2EAA2E;QAC3E,yEAAyE;QACzE,4EAA4E;QAC5E,MAAM,IAAI,GACR,QAAQ;YACR,WAAW,CAAC;gBACV,MAAM;gBACN,KAAK,EAAE,UAAU,CAAC,UAAU;gBAC5B,KAAK,EAAE,UAAU,CAAC,YAAY;gBAC9B,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,UAAU,CAAC,UAAU;gBACjC,QAAQ,EAAE,IAAI;aACf,CAAC;YACF,CAAC,UAAU,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3F,OAAO;YACL,UAAU;YACV,IAAI;YACJ,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvC,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import type { ContentBlock } from '@modelcontextprotocol/server';\n\nimport { basename, posix } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { paginate } from '../core/cursor.ts';\nimport { ErrorCode } from '../core/errors.ts';\nimport { pageTrailer } from '../core/fmt.ts';\nimport { globEntries } from '../core/glob.ts';\nimport { type EntryType, resolveEntryType } from '../core/path-utils.ts';\nimport type { PathGuard } from '../core/path.ts';\nimport { toPosixRelative } from '../core/path.ts';\nimport {\n CursorSchema,\n IncludeHidden,\n IncludeIgnored,\n OptionalPath,\n PositiveInt,\n} from '../core/schema.ts';\nimport type { JsonResourceResult } from '../core/store.ts';\nimport { putJsonResource } from '../core/store.ts';\nimport {\n DEFAULT_SEARCH_TIMEOUT_MS,\n DEFAULT_TREE_ENTRIES,\n MAX_LIST_ENTRIES,\n MAX_TREE_DEPTH,\n} from '../core/util.ts';\nimport { defineTool, type ToolCtx } from './define.ts';\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 entries: CollectedEntry[] = [];\n let scanned = 0;\n let totalEntries = 0;\n let totalFiles = 0;\n let totalDirectories = 0;\n const ceiling = options.pathGuard.allowedRootContaining(rootPath);\n\n for await (const entry of globEntries({\n cwd: rootPath,\n pattern: '**/*',\n skipIgnored: !options.includeIgnored,\n ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),\n signal: options.signal,\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 relPath = toPosixRelative(rootPath, entry.path);\n const name = basename(relPath);\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, page: readonly CollectedEntry[]): string {\n if (page.length === 0) return rootName;\n\n // The tree is drawn down from the root, but a later page (or a capped set)\n // can hold entries whose ancestors sat elsewhere; those would never be drawn.\n // Re-draw each missing ancestor as context so every entry stays locatable.\n const present = new Set(page.map((entry) => entry.relativePath));\n const entries = [...page];\n for (const entry of page) {\n for (\n let dir = posix.dirname(entry.relativePath);\n dir !== '.' && !present.has(dir);\n dir = posix.dirname(dir)\n ) {\n present.add(dir);\n entries.push({ name: posix.basename(dir), relativePath: dir, type: 'directory' });\n }\n }\n entries.sort(compareEntries);\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 + (child.type === 'directory' ? '/' : ''));\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;\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_TREE_ENTRIES)\n .describe(\n `Page size (default: ${String(DEFAULT_TREE_ENTRIES)}). Continue with nextCursor; an incomplete first page also carries resourceUri for the whole list.`,\n ),\n includeHidden: IncludeHidden,\n includeIgnored: IncludeIgnored,\n cursor: CursorSchema,\n});\n\ninterface ListPageMetadata {\n readonly path: string;\n readonly totalEntries: number;\n readonly totalFiles: number;\n readonly totalDirectories: number;\n}\n\ntype ListOutput = ListPageMetadata & {\n entries: CollectedEntry[];\n entryCount: number;\n resourceUri?: string;\n nextCursor?: string;\n};\n\nasync function handleList(\n args: z.infer<typeof ListInputSchema>,\n ctx: ToolCtx,\n): Promise<{\n structured: ListOutput;\n markdown: string;\n offset: number;\n link?: ContentBlock;\n}> {\n const path = args.path;\n const resolvedPath = ctx.fs.pathGuard.resolvePathOrRoot(path);\n const queryKey = JSON.stringify({\n method: 'list',\n path: resolvedPath,\n maxDepth: args.maxDepth,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\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: ctx.signal,\n pathGuard: ctx.fs.pathGuard,\n entryCap: MAX_LIST_ENTRIES,\n 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: (entries, metadata) => {\n const name = basename(metadata.path);\n // The stored tree is the whole collected set, bounded by the hard cap.\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(ctx.resourceStore, `${name} tree`, fullOutput);\n },\n });\n\n const resourceUri = paged.resource?.entry.uri;\n return {\n structured: {\n ...paged.metadata,\n entries: [...paged.page],\n entryCount: paged.page.length,\n ...(resourceUri !== undefined ? { resourceUri } : {}),\n ...(paged.nextCursor !== undefined ? { nextCursor: paged.nextCursor } : {}),\n },\n markdown: renderMarkdown(basename(paged.metadata.path), [...paged.page]),\n offset: paged.offset,\n ...(paged.resource ? { link: paged.resource.link } : {}),\n };\n}\n\nexport const LIST = defineTool<typeof ListInputSchema, ListOutput>({\n name: 'list',\n title: 'List',\n description:\n \"List a directory's files and subdirectories as a tree of names, like ls or tree. \" +\n 'stat returns sizes and dates.',\n input: ListInputSchema,\n readOnlyHint: true,\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, offset, link } = await handleList(args, ctx);\n // The tree is what the model reads, so position and the full-list URI ride\n // the text too — paging needs no second lookup. `pageTrailer` is the one\n // owner of that line, and it owes it on the last page as much as the first.\n const text =\n markdown +\n pageTrailer({\n offset,\n shown: structured.entryCount,\n total: structured.totalEntries,\n noun: 'entries',\n tool: 'list',\n nextCursor: structured.nextCursor,\n nextArgs: args,\n }) +\n (structured.resourceUri !== undefined ? `\\nfull tree at ${structured.resourceUri}` : '');\n return {\n structured,\n text,\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 { basename, posix } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { pageQueryKey, paginate } from '../core/cursor.ts';\nimport { ErrorCode, isFsError, isSkippableErrno, SKIPPABLE_FS_CODES } from '../core/errors.ts';\nimport { pageTrailer } from '../core/fmt.ts';\nimport { globEntries } from '../core/glob.ts';\nimport { type EntryType, resolveEntryType, toPosixRelative } from '../core/path-utils.ts';\nimport type { PathGuard } from '../core/path.ts';\nimport {\n CursorSchema,\n IncludeHidden,\n IncludeIgnored,\n OptionalPath,\n PositiveInt,\n} from '../core/schema.ts';\nimport type { JsonResourceResult } from '../core/store.ts';\nimport { putJsonResource } from '../core/store.ts';\nimport {\n DEFAULT_SEARCH_TIMEOUT_MS,\n DEFAULT_TREE_ENTRIES,\n MAX_LIST_ENTRIES,\n MAX_TREE_DEPTH,\n} from '../core/util.ts';\nimport { defineTool } from './define.ts';\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\n/**\n * True when a walked entry is inside the allowed directories and not sensitive,\n * on both its requested and resolved paths. Every entry is realpath-resolved: a\n * symlinked ancestor pointing outside the sandbox would otherwise pass the\n * lexical check (fs.glob follows symlinks and yields external entries as\n * non-symlink dirents). Escapes and sensitive targets throw ACCESS_DENIED and\n * skippable errors filter the entry; anything else is fatal. The accepted\n * TOCTOU window is documented on PathGuard.\n */\nasync function isEntryAccessible(guard: PathGuard, entryPath: string): Promise<boolean> {\n try {\n const { requestedPath, resolvedPath } = await guard.validateExistingPathDetailed(entryPath);\n return !guard.isSensitive(requestedPath) && !guard.isSensitive(resolvedPath);\n } catch (error) {\n if (isFsError(error)) {\n if (SKIPPABLE_FS_CODES.has(error.code)) return false;\n throw error;\n }\n if (isSkippableErrno(error)) return false;\n throw error;\n }\n}\n\nasync function collect(rootPath: string, options: CollectOptions): Promise<CollectResult> {\n const entries: CollectedEntry[] = [];\n let scanned = 0;\n let totalEntries = 0;\n let totalFiles = 0;\n let totalDirectories = 0;\n const ceiling = options.pathGuard.allowedRootContaining(rootPath);\n\n for await (const entry of globEntries({\n cwd: rootPath,\n pattern: '**/*',\n skipIgnored: !options.includeIgnored,\n ...(ceiling !== undefined ? { ignoreCeiling: ceiling } : {}),\n signal: options.signal,\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 relPath = toPosixRelative(rootPath, entry.path);\n const name = basename(relPath);\n\n const accessible = await isEntryAccessible(options.pathGuard, 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, page: readonly CollectedEntry[]): string {\n if (page.length === 0) return rootName;\n\n // The tree is drawn down from the root, but a later page (or a capped set)\n // can hold entries whose ancestors sat elsewhere; those would never be drawn.\n // Re-draw each missing ancestor as context so every entry stays locatable.\n const present = new Set(page.map((entry) => entry.relativePath));\n const entries = [...page];\n for (const entry of page) {\n for (\n let dir = posix.dirname(entry.relativePath);\n dir !== '.' && !present.has(dir);\n dir = posix.dirname(dir)\n ) {\n present.add(dir);\n entries.push({ name: posix.basename(dir), relativePath: dir, type: 'directory' });\n }\n }\n entries.sort(compareEntries);\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 + (child.type === 'directory' ? '/' : ''));\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;\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_TREE_ENTRIES)\n .describe(\n `Page size (default: ${String(DEFAULT_TREE_ENTRIES)}). Continue with nextCursor; an incomplete first page also carries resourceUri for the whole list.`,\n ),\n includeHidden: IncludeHidden,\n includeIgnored: IncludeIgnored,\n cursor: CursorSchema,\n});\n\ninterface ListPageMetadata {\n readonly path: string;\n readonly totalEntries: number;\n readonly totalFiles: number;\n readonly totalDirectories: number;\n}\n\ntype ListOutput = ListPageMetadata & {\n entries: CollectedEntry[];\n entryCount: number;\n resourceUri?: string;\n nextCursor?: string;\n};\n\nexport const LIST = defineTool<typeof ListInputSchema, ListOutput>({\n name: 'list',\n title: 'List',\n description:\n \"List a directory's files and subdirectories as a tree of names, like ls or tree. \" +\n 'stat returns sizes and dates.',\n input: ListInputSchema,\n readOnlyHint: true,\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 resolvedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n const paged = await paginate<CollectedEntry, ListPageMetadata, JsonResourceResult>({\n store: ctx.pageStore,\n queryKey: pageQueryKey('list', { ...args, path: resolvedPath }, ['cursor', 'maxEntries']),\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: ctx.signal,\n pathGuard: ctx.fs.pathGuard,\n entryCap: MAX_LIST_ENTRIES,\n 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: (entries, metadata) => {\n const name = basename(metadata.path);\n // The stored tree is the whole collected set, bounded by the hard cap.\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(ctx.resourceStore, `${name} tree`, fullOutput);\n },\n });\n\n const structured: ListOutput = {\n ...paged.metadata,\n entries: [...paged.page],\n entryCount: paged.page.length,\n ...(paged.resource ? { resourceUri: paged.resource.entry.uri } : {}),\n ...(paged.nextCursor !== undefined ? { nextCursor: paged.nextCursor } : {}),\n };\n // The tree is what the model reads, so position and the full-list URI ride\n // the text too — paging needs no second lookup. `pageTrailer` is the one\n // owner of that line, and it owes it on the last page as much as the first.\n const text =\n renderMarkdown(basename(paged.metadata.path), [...paged.page]) +\n pageTrailer({\n offset: paged.offset,\n shown: structured.entryCount,\n total: structured.totalEntries,\n noun: 'entries',\n tool: 'list',\n nextCursor: structured.nextCursor,\n nextArgs: args,\n }) +\n (structured.resourceUri !== undefined ? `\\nfull tree at ${structured.resourceUri}` : '');\n return {\n structured,\n text,\n ...(paged.resource ? { resources: [paged.resource.link] } : {}),\n };\n },\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAkfvD,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC,EACtD,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED,eAAO,MAAM,IAAI,mCAwCf,CAAC"}
1
+ {"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAsfvD,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC,EACtD,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED,eAAO,MAAM,IAAI,mCAwCf,CAAC"}
@@ -1,5 +1,4 @@
1
1
  import { isInputRequiredResult } from '@modelcontextprotocol/server';
2
- import { stat } from 'node:fs/promises';
3
2
  import { basename, dirname, relative, resolve } from 'node:path';
4
3
  import * as z from 'zod/v4';
5
4
  import { processInParallel } from "../core/concurrency.js";
@@ -7,7 +6,7 @@ import { ErrorCode, FsError, isFsError, isNodeError, Problem, rethrowIfAborted,
7
6
  import { joinRoster, pathLabel } from "../core/fmt.js";
8
7
  import { destExists } from "../core/fs.js";
9
8
  import { globEntries } from "../core/glob.js";
10
- import { confirmKey, describeRefusal, pendingRoundTrip, readAcceptedChoice, } from "../core/input-required.js";
9
+ import { confirmKey, pendingRoundTrip, readPendingVerdict } from "../core/input-required.js";
11
10
  import { isPathInsideDirectory, isSamePath, normalizeCaseForComparison, respellCaseOnlyTarget, } from "../core/path-utils.js";
12
11
  import { defaultFalseBoolean, RequiredPath } from "../core/schema.js";
13
12
  import { PARALLEL_CONCURRENCY } from "../core/util.js";
@@ -37,9 +36,10 @@ async function planTransfer(op, pair, fs, overwrite, signal) {
37
36
  let opSource;
38
37
  let validDest;
39
38
  try {
40
- ({ realSource, opSource } = await validateTransferSource(op, pair.source, fs));
39
+ const source = await validateTransferSource(op, pair.source, fs, signal);
40
+ ({ realSource, opSource } = source);
41
41
  validDest = await fs.pathGuard.validatePathForWrite(pair.destination);
42
- if (!(op === 'move' && opSource !== realSource) && (await stat(realSource)).isDirectory()) {
42
+ if (!(op === 'move' && opSource !== realSource) && source.isDirectory) {
43
43
  await assertTreeHasNoProtectedEntries(op, realSource, pair.source, validDest, fs, signal);
44
44
  }
45
45
  }
@@ -95,13 +95,12 @@ async function planTransfer(op, pair, fs, overwrite, signal) {
95
95
  */
96
96
  async function executeTransfer(op, plan, ctx, pendingSorted) {
97
97
  if (plan.pending) {
98
- const key = confirmKey(pendingSorted.indexOf(plan.validDest));
99
- const choice = readAcceptedChoice(ctx.inputResponses, key);
100
- if (choice === 'skip') {
98
+ const verdict = readPendingVerdict(ctx, pendingSorted, plan.validDest, 'overwrite');
99
+ if (verdict === 'skipped') {
101
100
  return { skipped: plan.pair.destination };
102
101
  }
103
- if (choice !== 'overwrite') {
104
- throw new FsError(ErrorCode.CANCELLED, `${VERB[op]} cancelled: overwrite of "${plan.pair.destination}" was ${describeRefusal(ctx, key)}`, plan.pair.destination);
102
+ if (verdict !== 'accepted') {
103
+ throw new FsError(ErrorCode.CANCELLED, `${VERB[op]} cancelled: overwrite of "${plan.pair.destination}" was ${verdict.refused}`, plan.pair.destination);
105
104
  }
106
105
  }
107
106
  // TOCTOU check before any mutation: a destination that did not exist when
@@ -174,13 +173,17 @@ async function runTransfers(items, ctx, op, overwrite) {
174
173
  }
175
174
  // One entry's (resolved) source overlapping another's destination means the
176
175
  // two race and a file is lost: a swap [A->B, B->A] or a chain [B->C, A->B].
176
+ // Destination subtrees must also be disjoint: two recursive copies otherwise
177
+ // race over shared descendants even when their destination paths differ.
177
178
  // Refuse both sides so the survivors are independent and can run concurrently.
178
179
  // isPathInsideDirectory covers equality and containment, case-folded.
179
180
  const touches = (x, y) => isPathInsideDirectory(x, y) || isPathInsideDirectory(y, x);
180
181
  const overlapping = new Set();
181
182
  for (const [i, a] of ready.entries()) {
182
183
  for (const b of ready.slice(i + 1)) {
183
- if (touches(a.resolvedSource, b.validDest) || touches(b.resolvedSource, a.validDest)) {
184
+ if (touches(a.resolvedSource, b.validDest) ||
185
+ touches(b.resolvedSource, a.validDest) ||
186
+ touches(a.validDest, b.validDest)) {
184
187
  overlapping.add(a);
185
188
  overlapping.add(b);
186
189
  }
@@ -268,14 +271,14 @@ function buildSummary(verb, results, failures, skipped) {
268
271
  * move and renames the link over that target, destroying it. A copy reads
269
272
  * through the link, so both views are the resolved one.
270
273
  */
271
- async function validateTransferSource(op, source, fs) {
274
+ async function validateTransferSource(op, source, fs, signal) {
272
275
  try {
273
- const realSource = await fs.pathGuard.validateExistingPath(source);
276
+ const { stats, validPath: realSource } = await fs.stat(source, { signal });
274
277
  const opSource = op === 'move' ? await fs.pathGuard.validatePathForDelete(source) : realSource;
275
- return { realSource, opSource };
278
+ return { realSource, opSource, isDirectory: stats.isDirectory() };
276
279
  }
277
280
  catch (error) {
278
- if (isFsError(error))
281
+ if (isFsError(error) || signal?.aborted)
279
282
  throw error;
280
283
  throw new FsError(ErrorCode.ACCESS_DENIED, `${VERB[op]} failed for ${source}`, source);
281
284
  }