@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.
Files changed (148) hide show
  1. package/dist/cli.d.ts +2 -14
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +14 -22
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/cursor.d.ts +2 -14
  6. package/dist/core/cursor.d.ts.map +1 -1
  7. package/dist/core/cursor.js +25 -39
  8. package/dist/core/cursor.js.map +1 -1
  9. package/dist/core/diff.d.ts +6 -0
  10. package/dist/core/diff.d.ts.map +1 -0
  11. package/dist/core/diff.js +21 -0
  12. package/dist/core/diff.js.map +1 -0
  13. package/dist/core/errors.d.ts +2 -31
  14. package/dist/core/errors.d.ts.map +1 -1
  15. package/dist/core/errors.js +15 -86
  16. package/dist/core/errors.js.map +1 -1
  17. package/dist/core/file-uri.d.ts +18 -2
  18. package/dist/core/file-uri.d.ts.map +1 -1
  19. package/dist/core/file-uri.js +34 -6
  20. package/dist/core/file-uri.js.map +1 -1
  21. package/dist/core/fmt.js +5 -5
  22. package/dist/core/fmt.js.map +1 -1
  23. package/dist/core/fs.d.ts +4 -13
  24. package/dist/core/fs.d.ts.map +1 -1
  25. package/dist/core/fs.js +4 -41
  26. package/dist/core/fs.js.map +1 -1
  27. package/dist/core/glob.d.ts +1 -7
  28. package/dist/core/glob.d.ts.map +1 -1
  29. package/dist/core/glob.js +1 -11
  30. package/dist/core/glob.js.map +1 -1
  31. package/dist/core/input-required.d.ts +2 -10
  32. package/dist/core/input-required.d.ts.map +1 -1
  33. package/dist/core/input-required.js +3 -12
  34. package/dist/core/input-required.js.map +1 -1
  35. package/dist/core/path-completer.d.ts.map +1 -1
  36. package/dist/core/path-completer.js +3 -7
  37. package/dist/core/path-completer.js.map +1 -1
  38. package/dist/core/path-discovery.js +2 -2
  39. package/dist/core/path-discovery.js.map +1 -1
  40. package/dist/core/path.d.ts +10 -25
  41. package/dist/core/path.d.ts.map +1 -1
  42. package/dist/core/path.js +28 -66
  43. package/dist/core/path.js.map +1 -1
  44. package/dist/core/primitives.d.ts +8 -3
  45. package/dist/core/primitives.d.ts.map +1 -1
  46. package/dist/core/primitives.js +10 -5
  47. package/dist/core/primitives.js.map +1 -1
  48. package/dist/core/read.d.ts +0 -16
  49. package/dist/core/read.d.ts.map +1 -1
  50. package/dist/core/read.js +19 -56
  51. package/dist/core/read.js.map +1 -1
  52. package/dist/core/schema.d.ts +1 -32
  53. package/dist/core/schema.d.ts.map +1 -1
  54. package/dist/core/schema.js +7 -66
  55. package/dist/core/schema.js.map +1 -1
  56. package/dist/core/search.d.ts +16 -5
  57. package/dist/core/search.d.ts.map +1 -1
  58. package/dist/core/search.js +118 -108
  59. package/dist/core/search.js.map +1 -1
  60. package/dist/core/sensitive.d.ts.map +1 -1
  61. package/dist/core/sensitive.js +7 -15
  62. package/dist/core/sensitive.js.map +1 -1
  63. package/dist/core/store.d.ts +1 -3
  64. package/dist/core/store.d.ts.map +1 -1
  65. package/dist/core/store.js +1 -5
  66. package/dist/core/store.js.map +1 -1
  67. package/dist/core/watcher-registry.d.ts +1 -8
  68. package/dist/core/watcher-registry.d.ts.map +1 -1
  69. package/dist/core/watcher-registry.js +3 -12
  70. package/dist/core/watcher-registry.js.map +1 -1
  71. package/dist/index.js +1 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/prompts.d.ts.map +1 -1
  74. package/dist/prompts.js +7 -33
  75. package/dist/prompts.js.map +1 -1
  76. package/dist/resources.d.ts +1 -1
  77. package/dist/resources.d.ts.map +1 -1
  78. package/dist/resources.js +3 -9
  79. package/dist/resources.js.map +1 -1
  80. package/dist/server.d.ts +0 -3
  81. package/dist/server.d.ts.map +1 -1
  82. package/dist/server.js +1 -5
  83. package/dist/server.js.map +1 -1
  84. package/dist/tools/batch.d.ts +5 -69
  85. package/dist/tools/batch.d.ts.map +1 -1
  86. package/dist/tools/batch.js +7 -117
  87. package/dist/tools/batch.js.map +1 -1
  88. package/dist/tools/create.d.ts.map +1 -1
  89. package/dist/tools/create.js +8 -18
  90. package/dist/tools/create.js.map +1 -1
  91. package/dist/tools/define.d.ts +1 -4
  92. package/dist/tools/define.d.ts.map +1 -1
  93. package/dist/tools/define.js +6 -24
  94. package/dist/tools/define.js.map +1 -1
  95. package/dist/tools/delete-file.d.ts.map +1 -1
  96. package/dist/tools/delete-file.js +6 -6
  97. package/dist/tools/delete-file.js.map +1 -1
  98. package/dist/tools/diff.d.ts.map +1 -1
  99. package/dist/tools/diff.js +3 -10
  100. package/dist/tools/diff.js.map +1 -1
  101. package/dist/tools/edit.d.ts.map +1 -1
  102. package/dist/tools/edit.js +43 -87
  103. package/dist/tools/edit.js.map +1 -1
  104. package/dist/tools/index.d.ts +0 -1
  105. package/dist/tools/index.d.ts.map +1 -1
  106. package/dist/tools/index.js +0 -1
  107. package/dist/tools/index.js.map +1 -1
  108. package/dist/tools/list.js +3 -2
  109. package/dist/tools/list.js.map +1 -1
  110. package/dist/tools/move.d.ts.map +1 -1
  111. package/dist/tools/move.js +169 -119
  112. package/dist/tools/move.js.map +1 -1
  113. package/dist/tools/patch.d.ts.map +1 -1
  114. package/dist/tools/patch.js +5 -23
  115. package/dist/tools/patch.js.map +1 -1
  116. package/dist/tools/progress.d.ts +0 -1
  117. package/dist/tools/progress.d.ts.map +1 -1
  118. package/dist/tools/progress.js +2 -8
  119. package/dist/tools/progress.js.map +1 -1
  120. package/dist/tools/read.d.ts.map +1 -1
  121. package/dist/tools/read.js +21 -25
  122. package/dist/tools/read.js.map +1 -1
  123. package/dist/tools/replace-in-files.d.ts.map +1 -1
  124. package/dist/tools/replace-in-files.js +25 -44
  125. package/dist/tools/replace-in-files.js.map +1 -1
  126. package/dist/tools/search-content.d.ts.map +1 -1
  127. package/dist/tools/search-content.js +17 -49
  128. package/dist/tools/search-content.js.map +1 -1
  129. package/dist/tools/search-files.d.ts.map +1 -1
  130. package/dist/tools/search-files.js +1 -2
  131. package/dist/tools/search-files.js.map +1 -1
  132. package/dist/tools/stat.d.ts.map +1 -1
  133. package/dist/tools/stat.js +5 -6
  134. package/dist/tools/stat.js.map +1 -1
  135. package/dist/transport/http-policy.d.ts +6 -0
  136. package/dist/transport/http-policy.d.ts.map +1 -1
  137. package/dist/transport/http-policy.js +2 -2
  138. package/dist/transport/http-policy.js.map +1 -1
  139. package/dist/transport/http.d.ts.map +1 -1
  140. package/dist/transport/http.js +4 -11
  141. package/dist/transport/http.js.map +1 -1
  142. package/dist/transport/stdio.js +1 -1
  143. package/dist/transport/stdio.js.map +1 -1
  144. package/dist/transport.d.ts +1 -3
  145. package/dist/transport.d.ts.map +1 -1
  146. package/dist/transport.js +5 -2
  147. package/dist/transport.js.map +1 -1
  148. package/package.json +1 -1
@@ -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;
@@ -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,MAAM,CAAC,MAAM,yBAAyB,GAAsB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEzF,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\nexport const ALL_REGISTERED_TOOL_NAMES: readonly string[] = ALL_TOOLS.map((t) => t.name);\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"]}
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"]}
@@ -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, resolveEntryType, } from '../core/glob.js';
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, entryType, [rootPath]);
66
+ const accessible = await options.pathGuard.isEntryAccessible(entry.path);
66
67
  if (!accessible)
67
68
  continue;
68
69
  totalEntries++;
@@ -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":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EACL,wBAAwB,EACxB,WAAW,EACX,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,YAAY,EACZ,QAAQ,IAAI,YAAY,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,SAAS,EACT,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,gBAAgB,GAAG,OAAO,CAAC,cAAc;QAC7C,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,MAAM,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtD,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;IAEzB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,WAAW,CAAC;QACpC,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,MAAM;QACf,eAAe,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,wBAAwB;QACvE,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,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAE3C,MAAM,SAAS,GAAc,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,SAAS,KAAK,WAAW,CAAC;QACxC,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE/B,IACE,gBAAgB;YAChB,oBAAoB,CAAC,gBAAgB,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE;gBAC3D,WAAW,EAAE,KAAK;aACnB,CAAC,EACF,CAAC;YACD,SAAS;QACX,CAAC;QACD,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,OAAkC;IAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE1C,+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,CAAC,CAAC;YAE5C,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;AAC7B,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAElD,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,kBAAkB,EAAE;IACnC,cAAc,EAAE,mBAAmB,EAAE;IACrC,MAAM,EAAE,YAAY;CACrB,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;IACtF,OAAO,EAAE,CAAC;SACP,KAAK,CACJ,CAAC,CAAC,YAAY,CAAC;QACb,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;QAChF,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,gDAAgD,CAAC;KAC9E,CAAC,CACH;SACA,QAAQ,CAAC,+EAA+E,CAAC;IAC5F,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,iEAAiE;IACjE,UAAU,EAAE,SAAS,CAAC,QAAQ,CAAC,6CAA6C,CAAC;IAC7E,YAAY,EAAE,SAAS,CAAC,QAAQ,CAAC,2DAA2D,CAAC;IAC7F,UAAU,EAAE,SAAS,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IAC7D,gBAAgB,EAAE,SAAS,CAAC,QAAQ,CAAC,mCAAmC,CAAC;IACzE,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,8FAA8F;QAC5F,6FAA6F;QAC7F,uDAAuD,CAC1D;IACH,UAAU,EAAE,gBAAgB;CAC7B,CAAC,CAAC;AASH,SAAS,YAAY,CAAC,IAAqC,EAAE,IAAY;IACvE,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,cAAc,EAAE,IAAI,CAAC,cAAc;KACpC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CACjB,OAAkC,EAClC,QAA0B,EAC1B,UAA8B,EAC9B,WAA+B;IAE/B,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC,MAAM;QAC1B,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;QAC3C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,IAAqC,EACrC,GAAY;IAMZ,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,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAClD,MAAM,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC;IAE9B,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,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,yBAAyB,CAAC;gBAC1D,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS;gBAC3B,QAAQ,EAAE,gBAAgB;gBAC1B,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1D,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,aAAa;YACxB,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;gBACpB,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACrC,qEAAqE;gBACrE,4CAA4C;gBAC5C,MAAM,UAAU,GAAG;oBACjB,OAAO;oBACP,QAAQ,EAAE,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC;oBACvC,YAAY,EAAE,QAAQ,CAAC,YAAY;oBACnC,UAAU,EAAE,QAAQ,CAAC,UAAU;oBAC/B,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;oBAC3C,GAAG,CAAC,QAAQ,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACvE,CAAC;gBACF,OAAO,eAAe,CAAC,aAAa,EAAE,GAAG,IAAI,OAAO,EAAE,UAAU,CAAC,CAAC;YACpE,CAAC;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,OAAO;QACL,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC;QAC/F,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QACxE,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,CAAC;IAC7B,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,WAAW,EACT,4EAA4E;QAC5E,4HAA4H;IAC9H,KAAK,EAAE,eAAe;IACtB,MAAM,EAAE,gBAAgB;IACxB,sEAAsE;IACtE,wEAAwE;IACxE,6EAA6E;IAC7E,0DAA0D;IAC1D,WAAW,EAAE;QACX,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,IAAI;QACpB,eAAe,EAAE,KAAK;QACtB,aAAa,EAAE,KAAK;KACrB;IACD,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,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACnE,0EAA0E;QAC1E,0EAA0E;QAC1E,uDAAuD;QACvD,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,UAAU,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,eAAe,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,UAAU,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACzC,OAAO,CAAC,IAAI,CACV,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,gCAAgC,UAAU,CAAC,WAAW,EAAE,CAC/H,CAAC;QACJ,CAAC;QACD,OAAO;YACL,UAAU;YACV,IAAI,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ;YAC5E,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 } 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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"AAsaA,eAAO,MAAM,IAAI,mCA4Cf,CAAC"}
1
+ {"version":3,"file":"move.d.ts","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"AA8eA,eAAO,MAAM,IAAI,mCA8Cf,CAAC"}
@@ -1,13 +1,14 @@
1
1
  import { isInputRequiredResult } from '@modelcontextprotocol/server';
2
- import { basename, dirname, resolve, sep } from 'node:path';
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, pairFailureSchema, RequiredPath } from '../core/schema.js';
10
- import { isTotalFailure, pairFailure, runOverPairs } from './batch.js';
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 = pairFailureSchema('moved', 'move');
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-moves and
39
- * moves into a subdirectory of the source, and stat the destination to decide
40
- * whether this move needs an overwrite confirmation. No `mkdir` happens here —
41
- * the old flow created the destination's parent before asking, which mutated
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 planMove(move, fs) {
45
- let renamePath;
46
- let realPath;
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
- validDest = await fs.pathGuard.validatePathForWrite(move.destination);
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(move, error) };
62
+ return { status: 'fail', failure: pairFailure(pair, error) };
59
63
  }
60
- // Comparisons run on the resolved source; only the fs call below uses
61
- // renamePath. validatePathForWrite resolves the destination through a
62
- // symlink too, so both sides of every check must be resolved to match.
63
- const resolvedSource = resolve(realPath);
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
- if (resolvedSource === resolvedDest) {
66
- // Self-move — silently skip.
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
- const normalizedDest = IS_CASE_INSENSITIVE_FS ? resolvedDest.toLowerCase() : resolvedDest;
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(move, new FsError(ErrorCode.INVALID_INPUT, 'Cannot move a directory into its own subdirectory', move.source)),
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 isCaseOnlyRename = isSamePath(resolvedSource, resolvedDest);
80
- const destExistedOriginally = !isCaseOnlyRename && (await destExists(fs, validDest, 'move'));
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: move, renamePath, validDest, isCaseOnlyRename, destExistedOriginally, pending },
86
+ plan: { pair, opSource, validDest, isCaseOnlyRename, destExistedOriginally, pending },
85
87
  };
86
88
  }
87
89
  /**
88
- * Phase 2 (mutation): confirm an overwrite if needed, create the destination's
89
- * parent, TOCTOU-check the destination, then rename (with cross-device fallback).
90
- * A declined/missing confirmation throws `CANCELLED`, collected as a per-move
91
- * failure by the caller. A destination that appeared between plan and rename
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 executeMove(plan, ctx, pendingSorted) {
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, `Move cancelled: overwrite of "${plan.pair.destination}" was declined or missing`, plan.pair.destination);
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
- const existsNow = await destExists(ctx.fs, plan.validDest, 'move');
109
- if (existsNow && !plan.destExistedOriginally) {
110
- throw new FsError(ErrorCode.CANCELLED, `Move cancelled: destination "${plan.pair.destination}" was created during confirmation.`, plan.pair.destination);
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
- await performRenameWithFallback(plan.renamePath, plan.validDest, ctx.fs, plan.pair.source);
115
- ctx.log?.('info', `move: ${plan.pair.source} -> ${plan.pair.destination}`, 'move');
116
- return { value: { from: plan.renamePath, to: plan.validDest } };
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
- catch (error) {
124
- return { status: 'fail', failure: pairFailure(copy, error) };
125
- }
126
- let validDest;
127
- try {
128
- validDest = await fs.pathGuard.validatePathForWrite(copy.destination);
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
- catch (error) {
131
- return { status: 'fail', failure: pairFailure(copy, error) };
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
- if (isSamePath(realSource, validDest)) {
134
- // Self-copy — noop
135
- return { status: 'noop' };
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
- if (isPathInsideDirectory(realSource, validDest)) {
138
- return {
139
- status: 'fail',
140
- failure: pairFailure(copy, new FsError(ErrorCode.INVALID_INPUT, 'Cannot copy a directory into its own subdirectory', copy.source)),
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 destExistedOriginally = await destExists(fs, validDest, 'copy');
144
- const pending = destExistedOriginally && !overwrite;
145
- return {
146
- status: 'plan',
147
- plan: { pair: copy, realSource, validDest, destExistedOriginally, pending },
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
- async function executeCopy(plan, ctx, pendingSorted, overwrite) {
151
- if (plan.pending && !overwrite) {
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
- if (choice !== 'overwrite') {
158
- throw new FsError(ErrorCode.CANCELLED, `Copy cancelled: overwrite of "${plan.pair.destination}" was declined or missing`, plan.pair.destination);
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
- // TOCTOU check before any mutation: a destination that did not exist when
162
- // planned but exists now was created during the confirmation gap.
163
- if ((await destExists(ctx.fs, plan.validDest, 'copy')) && !plan.destExistedOriginally) {
164
- throw new FsError(ErrorCode.CANCELLED, `Copy cancelled: destination "${plan.pair.destination}" was created during confirmation.`, plan.pair.destination);
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
- await ctx.fs.mkdir(dirname(plan.validDest), { recursive: true });
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 outcome = args.copy
184
- ? await runOverPairs(args.moves, ctx, {
185
- op: 'copy',
186
- plan: (pair) => planCopy(pair, ctx.fs, args.overwrite),
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 validateMoveSource(source, fs) {
280
+ async function validateTransferSource(op, source, fs) {
233
281
  try {
234
- const realPath = await fs.pathGuard.validateExistingPath(source);
235
- const renamePath = await fs.pathGuard.validatePathForDelete(source);
236
- return { renamePath, realPath };
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, `Move failed for ${source}`, source);
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, undefined, copyOrRemoveError);
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
- isError: isTotalFailure({ results: output.moves, skipped, failures }),
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
  });