@j0hanz/filesystem-mcp 2.8.1 → 2.9.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 (116) hide show
  1. package/README.md +33 -27
  2. package/dist/cli-help.d.ts.map +1 -1
  3. package/dist/cli-help.js +10 -1
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +10 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/concurrency.d.ts +4 -4
  9. package/dist/core/concurrency.d.ts.map +1 -1
  10. package/dist/core/concurrency.js +1 -1
  11. package/dist/core/concurrency.js.map +1 -1
  12. package/dist/core/config.d.ts +2 -0
  13. package/dist/core/config.d.ts.map +1 -1
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/errors.d.ts.map +1 -1
  16. package/dist/core/errors.js +7 -1
  17. package/dist/core/errors.js.map +1 -1
  18. package/dist/core/file-uri.d.ts +12 -3
  19. package/dist/core/file-uri.d.ts.map +1 -1
  20. package/dist/core/file-uri.js +35 -7
  21. package/dist/core/file-uri.js.map +1 -1
  22. package/dist/core/fmt.d.ts +2 -0
  23. package/dist/core/fmt.d.ts.map +1 -1
  24. package/dist/core/fmt.js +16 -0
  25. package/dist/core/fmt.js.map +1 -1
  26. package/dist/core/fs.d.ts +16 -1
  27. package/dist/core/fs.d.ts.map +1 -1
  28. package/dist/core/fs.js +127 -32
  29. package/dist/core/fs.js.map +1 -1
  30. package/dist/core/glob.d.ts +15 -0
  31. package/dist/core/glob.d.ts.map +1 -1
  32. package/dist/core/glob.js +119 -14
  33. package/dist/core/glob.js.map +1 -1
  34. package/dist/core/path-completer.d.ts.map +1 -1
  35. package/dist/core/path-completer.js +5 -3
  36. package/dist/core/path-completer.js.map +1 -1
  37. package/dist/core/path-utils.d.ts +42 -6
  38. package/dist/core/path-utils.d.ts.map +1 -1
  39. package/dist/core/path-utils.js +56 -10
  40. package/dist/core/path-utils.js.map +1 -1
  41. package/dist/core/path.d.ts +111 -7
  42. package/dist/core/path.d.ts.map +1 -1
  43. package/dist/core/path.js +453 -65
  44. package/dist/core/path.js.map +1 -1
  45. package/dist/core/read.d.ts +6 -0
  46. package/dist/core/read.d.ts.map +1 -1
  47. package/dist/core/read.js +26 -3
  48. package/dist/core/read.js.map +1 -1
  49. package/dist/core/schema.d.ts +9 -6
  50. package/dist/core/schema.d.ts.map +1 -1
  51. package/dist/core/schema.js +22 -19
  52. package/dist/core/schema.js.map +1 -1
  53. package/dist/core/search.d.ts +6 -0
  54. package/dist/core/search.d.ts.map +1 -1
  55. package/dist/core/search.js +161 -33
  56. package/dist/core/search.js.map +1 -1
  57. package/dist/core/sensitive.d.ts.map +1 -1
  58. package/dist/core/sensitive.js +64 -15
  59. package/dist/core/sensitive.js.map +1 -1
  60. package/dist/core/store.d.ts +1 -0
  61. package/dist/core/store.d.ts.map +1 -1
  62. package/dist/core/store.js +3 -0
  63. package/dist/core/store.js.map +1 -1
  64. package/dist/core/util.d.ts +12 -1
  65. package/dist/core/util.d.ts.map +1 -1
  66. package/dist/core/util.js +16 -1
  67. package/dist/core/util.js.map +1 -1
  68. package/dist/resources.d.ts.map +1 -1
  69. package/dist/resources.js +20 -11
  70. package/dist/resources.js.map +1 -1
  71. package/dist/server.js +1 -1
  72. package/dist/server.js.map +1 -1
  73. package/dist/tools/create.d.ts.map +1 -1
  74. package/dist/tools/create.js +121 -43
  75. package/dist/tools/create.js.map +1 -1
  76. package/dist/tools/define.d.ts +8 -4
  77. package/dist/tools/define.d.ts.map +1 -1
  78. package/dist/tools/define.js +36 -9
  79. package/dist/tools/define.js.map +1 -1
  80. package/dist/tools/delete.d.ts.map +1 -1
  81. package/dist/tools/delete.js +50 -11
  82. package/dist/tools/delete.js.map +1 -1
  83. package/dist/tools/diff.js +1 -1
  84. package/dist/tools/diff.js.map +1 -1
  85. package/dist/tools/edit.d.ts.map +1 -1
  86. package/dist/tools/edit.js +14 -12
  87. package/dist/tools/edit.js.map +1 -1
  88. package/dist/tools/find-files.d.ts.map +1 -1
  89. package/dist/tools/find-files.js +15 -9
  90. package/dist/tools/find-files.js.map +1 -1
  91. package/dist/tools/list.d.ts.map +1 -1
  92. package/dist/tools/list.js +141 -86
  93. package/dist/tools/list.js.map +1 -1
  94. package/dist/tools/move.d.ts.map +1 -1
  95. package/dist/tools/move.js +72 -26
  96. package/dist/tools/move.js.map +1 -1
  97. package/dist/tools/patch.js +13 -6
  98. package/dist/tools/patch.js.map +1 -1
  99. package/dist/tools/read.d.ts.map +1 -1
  100. package/dist/tools/read.js +113 -48
  101. package/dist/tools/read.js.map +1 -1
  102. package/dist/tools/replace-text.d.ts.map +1 -1
  103. package/dist/tools/replace-text.js +50 -35
  104. package/dist/tools/replace-text.js.map +1 -1
  105. package/dist/tools/search-text.d.ts.map +1 -1
  106. package/dist/tools/search-text.js +15 -33
  107. package/dist/tools/search-text.js.map +1 -1
  108. package/dist/tools/stat.d.ts.map +1 -1
  109. package/dist/tools/stat.js +26 -8
  110. package/dist/tools/stat.js.map +1 -1
  111. package/dist/transport/http.d.ts.map +1 -1
  112. package/dist/transport/http.js +1 -0
  113. package/dist/transport/http.js.map +1 -1
  114. package/dist/transport/stdio.js +1 -1
  115. package/dist/transport/stdio.js.map +1 -1
  116. package/package.json +5 -5
@@ -7,7 +7,7 @@ import { joinRoster, pathLabel } from "../core/fmt.js";
7
7
  import { destExists } from "../core/fs.js";
8
8
  import { globEntries } from "../core/glob.js";
9
9
  import { confirmKey, pendingRoundTrip, readPendingVerdict } from "../core/input-required.js";
10
- import { isPathInsideDirectory, isSamePath, normalizeCaseForComparison, respellCaseOnlyTarget, } from "../core/path-utils.js";
10
+ import { isPathInsideDirectory, isSamePath, respellSpellingOnlyTarget, unicodeMatchKey, } from "../core/path-utils.js";
11
11
  import { defaultFalseBoolean, RequiredPath } from "../core/schema.js";
12
12
  import { PARALLEL_CONCURRENCY } from "../core/util.js";
13
13
  import { defineTool } from "./define.js";
@@ -35,13 +35,11 @@ async function planTransfer(op, pair, fs, overwrite, signal) {
35
35
  let realSource;
36
36
  let opSource;
37
37
  let validDest;
38
+ let isDirectory;
38
39
  try {
39
40
  const source = await validateTransferSource(op, pair.source, fs, signal);
40
- ({ realSource, opSource } = source);
41
+ ({ realSource, opSource, isDirectory } = source);
41
42
  validDest = await fs.pathGuard.validatePathForWrite(pair.destination);
42
- if (!(op === 'move' && opSource !== realSource) && source.isDirectory) {
43
- await assertTreeHasNoProtectedEntries(op, realSource, pair.source, validDest, fs, signal);
44
- }
45
43
  }
46
44
  catch (error) {
47
45
  return { status: 'fail', failure: pairFailure(pair, error) };
@@ -51,27 +49,47 @@ async function planTransfer(op, pair, fs, overwrite, signal) {
51
49
  // too, so both sides of every check must be resolved to match.
52
50
  const resolvedSource = resolve(realSource);
53
51
  let resolvedDest = resolve(validDest);
54
- // validatePathForWrite realpaths an existing destination to its on-disk
55
- // spelling, so a case-only rename resolves to the source itself; re-spell
56
- // it with the requested basename before deciding self vs. rename.
57
- if (resolvedSource === resolvedDest) {
58
- validDest = respellCaseOnlyTarget(pair.destination, validDest);
52
+ // The entry the destination names, without following a final link. When it
53
+ // is the source's own entry in another case or Unicode spelling, this is a
54
+ // rename of that entry, re-spelled with the requested basename: of the link
55
+ // itself when the source is a link, which validatePathForWrite resolved to
56
+ // its target. Onto its target, in any spelling, a link is a self-move: a
57
+ // rename would replace that file with the link.
58
+ const destEntry = await fs.pathGuard
59
+ .validatePathForDelete(pair.destination)
60
+ .catch(() => undefined);
61
+ const sameEntry = destEntry !== undefined && isSamePath(destEntry, opSource);
62
+ if (sameEntry) {
63
+ validDest = respellSpellingOnlyTarget(pair.destination, opSource);
59
64
  resolvedDest = resolve(validDest);
60
65
  }
61
- const isSelf = resolvedSource === resolvedDest;
62
- const isCaseOnlyRename = !isSelf && isSamePath(resolvedSource, resolvedDest);
63
- // A copy onto a case-only variant of its own source is the same file, so it
64
- // is a no-op; the same rename is real work for move and proceeds.
65
- if (isSelf || (isCaseOnlyRename && op === 'copy')) {
66
+ // On the final destination spelling: a re-spelled directory moves its
67
+ // entries in or out of a spelling-specific allow or deny rule.
68
+ if (!(op === 'move' && opSource !== realSource) && isDirectory) {
69
+ try {
70
+ await assertTreeHasNoProtectedEntries(op, realSource, pair.source, validDest, fs, signal);
71
+ }
72
+ catch (error) {
73
+ return { status: 'fail', failure: pairFailure(pair, error) };
74
+ }
75
+ }
76
+ const isSelf = sameEntry ? resolvedDest === resolve(opSource) : resolvedSource === resolvedDest;
77
+ // Only a destination naming the source's own entry is renamed in place:
78
+ // onto a separate `a\u202fb.txt`, a move of `a\u00a0b.txt` is an overwrite.
79
+ const isSpellingOnlyRename = !isSelf && (sameEntry || isSamePath(resolvedSource, resolvedDest));
80
+ // A copy onto a case-only or Unicode-only variant of its own source is the
81
+ // same file, so it is a no-op; the same rename is real work for move and
82
+ // proceeds.
83
+ if (isSelf || (isSpellingOnlyRename && op === 'copy')) {
66
84
  return { status: 'noop' };
67
85
  }
68
- if (!isCaseOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {
86
+ if (!isSpellingOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {
69
87
  return {
70
88
  status: 'fail',
71
89
  failure: pairFailure(pair, new FsError(ErrorCode.INVALID_INPUT, `Cannot ${op} a directory into its own subdirectory`, pair.source)),
72
90
  };
73
91
  }
74
- const destExistedOriginally = !isCaseOnlyRename && (await destExists(fs, validDest, op));
92
+ const destExistedOriginally = !isSpellingOnlyRename && (await destExists(fs, validDest, op));
75
93
  const pending = destExistedOriginally && !overwrite;
76
94
  return {
77
95
  status: 'plan',
@@ -80,7 +98,7 @@ async function planTransfer(op, pair, fs, overwrite, signal) {
80
98
  opSource,
81
99
  resolvedSource,
82
100
  validDest,
83
- isCaseOnlyRename,
101
+ isSpellingOnlyRename,
84
102
  destExistedOriginally,
85
103
  pending,
86
104
  },
@@ -105,7 +123,7 @@ async function executeTransfer(op, plan, ctx, pendingSorted) {
105
123
  }
106
124
  // TOCTOU check before any mutation: a destination that did not exist when
107
125
  // planned but exists now was created during the confirmation gap.
108
- if (!plan.isCaseOnlyRename &&
126
+ if (!plan.isSpellingOnlyRename &&
109
127
  !plan.destExistedOriginally &&
110
128
  (await destExists(ctx.fs, plan.validDest, op))) {
111
129
  throw new FsError(ErrorCode.CANCELLED, `${VERB[op]} cancelled: destination "${plan.pair.destination}" was created during confirmation.`, plan.pair.destination);
@@ -163,7 +181,9 @@ async function runTransfers(items, ctx, op, overwrite) {
163
181
  const seenDest = new Set();
164
182
  const ready = [];
165
183
  for (const candidate of candidates) {
166
- const destKey = normalizeCaseForComparison(candidate.validDest);
184
+ // Unicode-folded too: two spellings of one missing name land on one file
185
+ // once the first move creates it (#101).
186
+ const destKey = unicodeMatchKey(candidate.validDest);
167
187
  if (seenDest.has(destKey)) {
168
188
  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)));
169
189
  continue;
@@ -176,12 +196,21 @@ async function runTransfers(items, ctx, op, overwrite) {
176
196
  // Destination subtrees must also be disjoint: two recursive copies otherwise
177
197
  // race over shared descendants even when their destination paths differ.
178
198
  // Refuse both sides so the survivors are independent and can run concurrently.
179
- // isPathInsideDirectory covers equality and containment, case-folded.
180
- const touches = (x, y) => isPathInsideDirectory(x, y) || isPathInsideDirectory(y, x);
199
+ // isPathInsideDirectory covers equality and containment, case-folded; the
200
+ // keys fold Unicode spelling too, parents included. Two moves compare the
201
+ // entries they rename (opSource), not where a link points, and unfolded:
202
+ // validated sources are on-disk names, so two that fold alike are distinct.
203
+ const touches = (x, y) => {
204
+ const [keyX, keyY] = [unicodeMatchKey(x), unicodeMatchKey(y)];
205
+ return isPathInsideDirectory(keyX, keyY) || isPathInsideDirectory(keyY, keyX);
206
+ };
181
207
  const overlapping = new Set();
182
208
  for (const [i, a] of ready.entries()) {
183
209
  for (const b of ready.slice(i + 1)) {
184
- if (touches(a.resolvedSource, b.validDest) ||
210
+ if ((op === 'move' &&
211
+ (isPathInsideDirectory(a.opSource, b.opSource) ||
212
+ isPathInsideDirectory(b.opSource, a.opSource))) ||
213
+ touches(a.resolvedSource, b.validDest) ||
185
214
  touches(b.resolvedSource, a.validDest) ||
186
215
  touches(a.validDest, b.validDest)) {
187
216
  overlapping.add(a);
@@ -273,9 +302,23 @@ function buildSummary(verb, results, failures, skipped) {
273
302
  */
274
303
  async function validateTransferSource(op, source, fs, signal) {
275
304
  try {
305
+ if (op === 'move') {
306
+ const opSource = await fs.pathGuard.validatePathForDelete(source);
307
+ try {
308
+ const { stats, validPath: realSource } = await fs.stat(source, { signal });
309
+ return { realSource, opSource, isDirectory: stats.isDirectory() };
310
+ }
311
+ catch (err) {
312
+ if (!isFsError(err) || err.code !== ErrorCode.NOT_FOUND)
313
+ throw err;
314
+ const { stats } = await fs.lstat(source, { signal });
315
+ if (!stats.isSymbolicLink())
316
+ throw err;
317
+ return { realSource: opSource, opSource, isDirectory: false };
318
+ }
319
+ }
276
320
  const { stats, validPath: realSource } = await fs.stat(source, { signal });
277
- const opSource = op === 'move' ? await fs.pathGuard.validatePathForDelete(source) : realSource;
278
- return { realSource, opSource, isDirectory: stats.isDirectory() };
321
+ return { realSource, opSource: realSource, isDirectory: stats.isDirectory() };
279
322
  }
280
323
  catch (error) {
281
324
  if (isFsError(error) || signal?.aborted)
@@ -376,7 +419,10 @@ export const MOVE = defineTool({
376
419
  }
377
420
  return { label, subject: `${String(args.moves.length)} files` };
378
421
  },
379
- accessPaths: (args) => args.moves.flatMap((m) => [m.source, m.destination]),
422
+ mapPaths: (args, fn) => ({
423
+ ...args,
424
+ moves: args.moves.map((m) => ({ ...m, source: fn(m.source), destination: fn(m.destination) })),
425
+ }),
380
426
  run: async (args, ctx) => {
381
427
  const op = args.copy ? 'copy' : 'move';
382
428
  // `overwrite` is copy-only; move has no confirmation bypass.
@@ -1 +1 @@
1
- {"version":3,"file":"move.js","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\nimport { isInputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { basename, dirname, relative, resolve } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport {\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n Problem,\n rethrowIfAborted,\n} from '../core/errors.ts';\nimport { joinRoster, pathLabel } from '../core/fmt.ts';\nimport { destExists } from '../core/fs.ts';\nimport type { GuardedFileSystem } from '../core/fs.ts';\nimport { globEntries } from '../core/glob.ts';\nimport { confirmKey, pendingRoundTrip, readPendingVerdict } from '../core/input-required.ts';\nimport {\n isPathInsideDirectory,\n isSamePath,\n normalizeCaseForComparison,\n respellCaseOnlyTarget,\n} from '../core/path-utils.ts';\nimport { defaultFalseBoolean, RequiredPath } from '../core/schema.ts';\nimport { PARALLEL_CONCURRENCY } from '../core/util.ts';\nimport type { ToolCtx } from './define.ts';\nimport { defineTool } from './define.ts';\n\nconst MoveItemSchema = z.strictObject({\n source: RequiredPath.describe('Path of the file or directory to move or copy'),\n destination: RequiredPath.describe('Destination path'),\n});\n\ninterface MoveItemResult {\n from: string;\n to: string;\n}\n\nconst MoveInputSchema = z.strictObject({\n moves: z.array(MoveItemSchema).min(1).max(100).describe('Operations to perform (max 100)'),\n copy: defaultFalseBoolean('Copy instead of move; sources are left in place'),\n overwrite: defaultFalseBoolean(\n 'Copy mode only: overwrite existing destinations without confirmation',\n ),\n});\n\ninterface MoveFailureItem {\n source: string;\n destination: string;\n error: Problem;\n}\n\ninterface MoveOutput {\n moves: MoveItemResult[];\n failures?: MoveFailureItem[];\n skipped?: string[];\n}\n\ntype PairOp = 'move' | 'copy';\n\nconst VERB: Readonly<Record<PairOp, string>> = { move: 'Move', copy: 'Copy' };\n\n/** A move or copy pre-checked up to the point a confirmation decision is needed. */\ninterface TransferPlan {\n pair: { source: string; destination: string };\n /**\n * The path the mutation runs on: the link itself for a move, the resolved\n * target for a copy. Every collision check uses the resolved source instead\n * — see {@link validateTransferSource}.\n */\n opSource: string;\n /** Symlink-resolved source, for cross-entry overlap detection (opSource may be the link). */\n resolvedSource: string;\n validDest: string;\n /**\n * A rename that only changes the destination's case. Move must perform it —\n * it is real work — so it is exempt from the destination-exists checks that\n * would otherwise read the source itself as an existing destination. Never\n * true for a copy, which treats a case-only target as a self-copy and skips.\n */\n isCaseOnlyRename: boolean;\n /** Whether the destination existed when planned (drives the TOCTOU guard). */\n destExistedOriginally: boolean;\n /** Destination exists and is not a self/case-only target → needs overwrite confirmation. */\n pending: boolean;\n}\n\n/**\n * Phase 1 (no mutation): validate source and destination, reject self-targets\n * and transfers into a subdirectory of the source, and stat the destination to\n * decide whether this pair needs an overwrite confirmation. No `mkdir` happens\n * here — the old flow created the destination's parent before asking, which\n * mutated the filesystem before a confirmation; that now waits for phase 2 (R14).\n *\n * `overwrite` is copy-only — move has no confirmation bypass — so a move always\n * passes false.\n */\nasync function planTransfer(\n op: PairOp,\n pair: { source: string; destination: string },\n fs: ToolCtx['fs'],\n overwrite: boolean,\n signal: AbortSignal | undefined,\n): Promise<TransferPlanResult> {\n let realSource: string;\n let opSource: string;\n let validDest: string;\n try {\n const source = await validateTransferSource(op, pair.source, fs, signal);\n ({ realSource, opSource } = source);\n validDest = await fs.pathGuard.validatePathForWrite(pair.destination);\n if (!(op === 'move' && opSource !== realSource) && source.isDirectory) {\n await assertTreeHasNoProtectedEntries(op, realSource, pair.source, validDest, fs, signal);\n }\n } catch (error) {\n return { status: 'fail', failure: pairFailure(pair, error) };\n }\n\n // Comparisons run on the resolved source; only the fs call in phase 2 uses\n // opSource. validatePathForWrite resolves the destination through a symlink\n // too, so both sides of every check must be resolved to match.\n const resolvedSource = resolve(realSource);\n let resolvedDest = resolve(validDest);\n\n // validatePathForWrite realpaths an existing destination to its on-disk\n // spelling, so a case-only rename resolves to the source itself; re-spell\n // it with the requested basename before deciding self vs. rename.\n if (resolvedSource === resolvedDest) {\n validDest = respellCaseOnlyTarget(pair.destination, validDest);\n resolvedDest = resolve(validDest);\n }\n\n const isSelf = resolvedSource === resolvedDest;\n const isCaseOnlyRename = !isSelf && isSamePath(resolvedSource, resolvedDest);\n\n // A copy onto a case-only variant of its own source is the same file, so it\n // is a no-op; the same rename is real work for move and proceeds.\n if (isSelf || (isCaseOnlyRename && op === 'copy')) {\n return { status: 'noop' };\n }\n\n if (!isCaseOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {\n return {\n status: 'fail',\n failure: pairFailure(\n pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `Cannot ${op} a directory into its own subdirectory`,\n pair.source,\n ),\n ),\n };\n }\n\n const destExistedOriginally = !isCaseOnlyRename && (await destExists(fs, validDest, op));\n const pending = destExistedOriginally && !overwrite;\n\n return {\n status: 'plan',\n plan: {\n pair,\n opSource,\n resolvedSource,\n validDest,\n isCaseOnlyRename,\n destExistedOriginally,\n pending,\n },\n };\n}\n\n/**\n * Phase 2 (mutation): confirm an overwrite if needed, TOCTOU-check the\n * destination, create its parent, then rename (with cross-device fallback) or\n * copy. A declined/missing confirmation throws `CANCELLED`, collected as a\n * per-pair failure by the caller. A destination that appeared between plan and\n * mutation (created during the confirmation gap) also fails closed.\n */\nasync function executeTransfer(\n op: PairOp,\n plan: TransferPlan,\n ctx: Pick<ToolCtx, 'fs' | 'signal' | 'inputResponses' | 'droppedInputResponseKeys' | 'log'>,\n pendingSorted: readonly string[],\n): Promise<TransferExecResult> {\n if (plan.pending) {\n const verdict = readPendingVerdict(ctx, pendingSorted, plan.validDest, 'overwrite');\n if (verdict === 'skipped') {\n return { skipped: plan.pair.destination };\n }\n if (verdict !== 'accepted') {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: overwrite of \"${plan.pair.destination}\" was ${verdict.refused}`,\n plan.pair.destination,\n );\n }\n }\n\n // TOCTOU check before any mutation: a destination that did not exist when\n // planned but exists now was created during the confirmation gap.\n if (\n !plan.isCaseOnlyRename &&\n !plan.destExistedOriginally &&\n (await destExists(ctx.fs, plan.validDest, op))\n ) {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: destination \"${plan.pair.destination}\" was created during confirmation.`,\n plan.pair.destination,\n );\n }\n\n await ctx.fs.mkdir(dirname(plan.validDest), { recursive: true });\n\n if (op === 'move') {\n await performRenameWithFallback(plan.opSource, plan.validDest, ctx.fs, plan.pair.source);\n ctx.log('info', `move: ${plan.pair.source} -> ${plan.pair.destination}`, 'move');\n } else {\n await ctx.fs.cp(plan.opSource, plan.validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n force: true,\n });\n }\n return { value: { from: plan.opSource, to: plan.validDest } };\n}\n\ntype TransferPlanResult =\n | { status: 'fail'; failure: MoveFailureItem }\n | { status: 'noop' }\n | { status: 'plan'; plan: TransferPlan };\n\n/** A discriminated union, so the fold below narrows without a generic cast. */\ntype TransferExecResult = { readonly skipped: string } | { readonly value: MoveItemResult };\n\nfunction pairFailure(\n pair: { source: string; destination: string },\n error: unknown,\n): MoveFailureItem {\n return {\n source: pair.source,\n destination: pair.destination,\n error: Problem.fromUnknown(error, ErrorCode.UNKNOWN, pair.source),\n };\n}\n\n/**\n * The source→destination sibling of `runOverPaths`: plan every pair, fail closed\n * on a duplicated destination, round-trip the overwrite confirmations as one\n * set, then execute what survived in parallel. `copy` and `move` differ only in\n * their `plan` and `execute` callbacks.\n */\nasync function runTransfers(\n items: readonly z.infer<typeof MoveItemSchema>[],\n ctx: ToolCtx,\n op: PairOp,\n overwrite: boolean,\n): Promise<\n | { results: MoveItemResult[]; skipped: string[]; failures: MoveFailureItem[] }\n | InputRequiredResult\n> {\n const { results: planned, errors: planErrors } = await processInParallel(\n items,\n (pair) => planTransfer(op, pair, ctx.fs, overwrite, ctx.signal),\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n // plan callbacks are fail-closed (they return { status: 'fail' }, never\n // throw), so planErrors should always be empty. Surface the first rather\n // than silently dropping the item if a future plan callback violates that.\n const firstPlanError = planErrors[0];\n if (firstPlanError) throw firstPlanError.error;\n\n const failures: MoveFailureItem[] = [];\n const candidates: TransferPlan[] = [];\n for (const { value } of planned) {\n if (value.status === 'fail') failures.push(value.failure);\n else if (value.status === 'plan') candidates.push(value.plan);\n // 'noop' (self-copy / self-move) is silently skipped.\n }\n\n // Two sources targeting the same destination in one batch would otherwise\n // collapse to a single shared overwrite confirmation and let the second\n // one silently clobber the first's freshly-written content. Fail closed:\n // only the first plan per destination proceeds; later ones targeting the\n // same destination are reported as a per-item failure.\n const seenDest = new Set<string>();\n const ready: TransferPlan[] = [];\n for (const candidate of candidates) {\n const destKey = normalizeCaseForComparison(candidate.validDest);\n if (seenDest.has(destKey)) {\n failures.push(\n pairFailure(\n candidate.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${VERB[op]} cancelled: another entry in this batch already targets destination \"${candidate.pair.destination}\"`,\n candidate.pair.destination,\n ),\n ),\n );\n continue;\n }\n seenDest.add(destKey);\n ready.push(candidate);\n }\n\n // One entry's (resolved) source overlapping another's destination means the\n // two race and a file is lost: a swap [A->B, B->A] or a chain [B->C, A->B].\n // Destination subtrees must also be disjoint: two recursive copies otherwise\n // race over shared descendants even when their destination paths differ.\n // Refuse both sides so the survivors are independent and can run concurrently.\n // isPathInsideDirectory covers equality and containment, case-folded.\n const touches = (x: string, y: string): boolean =>\n isPathInsideDirectory(x, y) || isPathInsideDirectory(y, x);\n const overlapping = new Set<TransferPlan>();\n for (const [i, a] of ready.entries()) {\n for (const b of ready.slice(i + 1)) {\n if (\n touches(a.resolvedSource, b.validDest) ||\n touches(b.resolvedSource, a.validDest) ||\n touches(a.validDest, b.validDest)\n ) {\n overlapping.add(a);\n overlapping.add(b);\n }\n }\n }\n const safe: TransferPlan[] = [];\n for (const plan of ready) {\n if (!overlapping.has(plan)) {\n safe.push(plan);\n continue;\n }\n failures.push(\n pairFailure(\n plan.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${VERB[op]} cancelled: \"${plan.pair.source}\" or \"${plan.pair.destination}\" overlaps another entry's source or destination in this batch. Issue dependent moves one at a time.`,\n plan.pair.source,\n ),\n ),\n );\n }\n\n const pendingSorted = safe\n .filter((p) => p.pending)\n .map((p) => p.validDest)\n .sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip(ctx, {\n op,\n pending: pendingSorted,\n buildInputs: (dests) =>\n dests.map((dest, i) => ({\n key: confirmKey(i),\n message:\n op === 'move'\n ? `\"${dest}\" already exists. Overwrite it?`\n : `Destination \"${dest}\" already exists. Overwrite it?`,\n choices: ['overwrite', 'skip'],\n })),\n });\n if (round !== undefined) return round;\n }\n\n const total = safe.length;\n let completed = 0;\n const tick = (): void => {\n completed += 1;\n ctx.onProgress({ current: completed, total });\n };\n const { results: execResults, errors: execErrors } = await processInParallel(\n safe,\n async (readyPlan) => {\n try {\n return await executeTransfer(op, readyPlan, ctx, pendingSorted);\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { error, index } of execErrors) {\n rethrowIfAborted(error);\n const failed = safe[index];\n if (failed) failures.push(pairFailure(failed.pair, error));\n }\n\n const results: MoveItemResult[] = [];\n const skipped: string[] = [];\n for (const { value } of execResults) {\n if ('skipped' in value) skipped.push(value.skipped);\n else results.push(value.value);\n }\n\n return { results, skipped, failures };\n}\n\nfunction buildSummary(\n verb: 'move' | 'copy',\n results: readonly MoveItemResult[],\n failures: readonly MoveFailureItem[],\n skipped: readonly string[],\n): string {\n const failCount = failures.length;\n // Name each pair. \"move: 2 items\" left the caller to open structuredContent\n // to learn which two, and a partial failure was unreadable without it.\n // Skipped destinations are an outcome the user chose, not a failure — name\n // them, or an all-skipped call reads as \"copy: nothing\".\n const tokens = [\n ...results.map((r) => `${pathLabel(r.from)} → ${pathLabel(r.to)}`),\n ...skipped.map((dest) => `${pathLabel(dest)} SKIPPED`),\n ];\n const parts = [`${verb}: ${joinRoster(tokens) || 'nothing'}`];\n if (failCount > 0) parts.push(`${String(failCount)} failed`);\n return parts.join(' · ');\n}\n\ninterface TransferSource {\n /** Symlink resolved — the identity used for every same-target comparison. */\n realSource: string;\n /** The path the mutation runs on: symlink preserved for move, resolved for copy. */\n opSource: string;\n /** Whether the resolved source is a directory (from the same guarded stat). */\n isDirectory: boolean;\n}\n\n/**\n * A move needs two views of its source. Renaming must operate on the link\n * itself, or moving a symlink would rename the file it points at and leave the\n * link dangling. Every collision check must instead compare the resolved\n * target, or a link moved onto itself (or onto its own target) reads as a real\n * move and renames the link over that target, destroying it. A copy reads\n * through the link, so both views are the resolved one.\n */\nasync function validateTransferSource(\n op: PairOp,\n source: string,\n fs: ToolCtx['fs'],\n signal: AbortSignal | undefined,\n): Promise<TransferSource> {\n try {\n const { stats, validPath: realSource } = await fs.stat(source, { signal });\n const opSource = op === 'move' ? await fs.pathGuard.validatePathForDelete(source) : realSource;\n return { realSource, opSource, isDirectory: stats.isDirectory() };\n } catch (error) {\n if (isFsError(error) || signal?.aborted) throw error;\n throw new FsError(ErrorCode.ACCESS_DENIED, `${VERB[op]} failed for ${source}`, source);\n }\n}\n\n/**\n * A directory transfer may proceed only when every entry keeps the same\n * sensitive-file status before and after relocation. Path-based rules\n * (`secrets/**`, `.aws/credentials`) follow the path, not the file, so refuse\n * any entry that would leave or enter the protected set. Walk the tree once\n * before any mutation and fail the pair closed if anything changes status.\n * Name-based rules need no help — they match after the move too.\n */\nasync function assertTreeHasNoProtectedEntries(\n op: PairOp,\n realSource: string,\n requestedSource: string,\n validDest: string,\n fs: ToolCtx['fs'],\n signal: AbortSignal | undefined,\n): Promise<void> {\n let protectedCount = 0;\n const entries = globEntries({\n cwd: realSource,\n pattern: '**/*',\n includeHidden: true,\n skipIgnored: false,\n onlyFiles: false,\n ...(signal ? { signal } : {}),\n });\n try {\n for await (const entry of entries) {\n signal?.throwIfAborted();\n const entryRelativePath = relative(realSource, entry.path);\n const requestedSourcePath = resolve(requestedSource, entryRelativePath);\n const destinationPath = resolve(validDest, entryRelativePath);\n const protectedBefore =\n fs.pathGuard.isSensitive(entry.path) || fs.pathGuard.isSensitive(requestedSourcePath);\n const protectedAfter = fs.pathGuard.isSensitive(destinationPath);\n if (protectedBefore !== protectedAfter) {\n protectedCount++;\n break;\n }\n }\n } catch (error) {\n rethrowIfAborted(error);\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `${VERB[op]} refused: the directory could not be fully inspected for protected entries`,\n requestedSource,\n error,\n );\n }\n if (protectedCount > 0) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `${VERB[op]} refused: the directory contains protected entries matching the sensitive-file policy; move or copy individual files instead`,\n requestedSource,\n );\n }\n}\n\nexport async function performRenameWithFallback(\n validSource: string,\n validDest: string,\n fsOps: Pick<GuardedFileSystem, 'rename' | 'cp' | 'rm'>,\n originalSource: string,\n): Promise<void> {\n try {\n await fsOps.rename(validSource, validDest);\n } catch (error: unknown) {\n rethrowIfAborted(error);\n\n if (!isNodeError(error) || error.code !== 'EXDEV') {\n throw error;\n }\n\n // EXDEV: cross-device rename. Copy then remove, preserving symlinks and\n // timestamps so link semantics survive the move.\n let copied = false;\n try {\n await fsOps.cp(validSource, validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n });\n copied = true;\n await fsOps.rm(validSource, { recursive: true, force: true });\n } catch (copyOrRemoveError) {\n rethrowIfAborted(copyOrRemoveError);\n if (copied) {\n // cp succeeded but rm failed: the destination already holds a complete\n // copy and the source remains. Surface the rm error as the cause so the\n // caller can recover (clean up the duplicate) instead of a silent generic\n // failure that hides the partial completion.\n const baseProblem = Problem.fromUnknown(\n copyOrRemoveError,\n ErrorCode.UNKNOWN,\n originalSource,\n );\n throw new FsError(\n baseProblem.code,\n `Cross-device move of ${originalSource}: copy succeeded but source removal failed (destination holds a copy): ${baseProblem.message}`,\n originalSource,\n copyOrRemoveError,\n );\n }\n throw copyOrRemoveError;\n }\n }\n}\n\nexport const MOVE = defineTool<typeof MoveInputSchema, MoveOutput>({\n name: 'move',\n title: 'Move or Copy Files',\n description:\n 'Move, rename, or copy files and directories, like mv or cp -r. ' +\n 'Each destination is the full new path, not a folder to move into; missing parent directories are created.',\n input: MoveInputSchema,\n readOnlyHint: false,\n progress: (args) => {\n const label = args.copy ? 'Copy' : 'Move';\n if (args.moves.length === 1) {\n const move = args.moves[0];\n return {\n label,\n subject: `${basename(move?.source ?? '')} → ${basename(move?.destination ?? '')}`,\n };\n }\n return { label, subject: `${String(args.moves.length)} files` };\n },\n accessPaths: (args) => args.moves.flatMap((m) => [m.source, m.destination]),\n run: async (args, ctx) => {\n const op: PairOp = args.copy ? 'copy' : 'move';\n // `overwrite` is copy-only; move has no confirmation bypass.\n const outcome = await runTransfers(args.moves, ctx, op, args.copy && args.overwrite);\n // input_required is a return value, not a completed call: surface it\n // verbatim so the executor short-circuits before building a CallToolResult.\n if (isInputRequiredResult(outcome)) return outcome;\n const { results, skipped, failures } = outcome;\n return {\n structured: {\n moves: results,\n ...(failures.length > 0 ? { failures } : {}),\n ...(skipped.length > 0 ? { skipped } : {}),\n },\n text: buildSummary(op, results, failures, skipped),\n // Every requested pair failed: no move, no copy, no user-chosen skip - the\n // call did nothing. A skip is work the caller asked for, so it counts.\n isError: failures.length > 0 && results.length + skipped.length === 0,\n };\n },\n});\n"]}
1
+ {"version":3,"file":"move.js","sourceRoot":"","sources":["../../src/tools/move.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InputRequiredResult } from '@modelcontextprotocol/server';\nimport { isInputRequiredResult } from '@modelcontextprotocol/server';\n\nimport { basename, dirname, relative, resolve } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { processInParallel } from '../core/concurrency.ts';\nimport {\n ErrorCode,\n FsError,\n isFsError,\n isNodeError,\n Problem,\n rethrowIfAborted,\n} from '../core/errors.ts';\nimport { joinRoster, pathLabel } from '../core/fmt.ts';\nimport { destExists } from '../core/fs.ts';\nimport type { GuardedFileSystem } from '../core/fs.ts';\nimport { globEntries } from '../core/glob.ts';\nimport { confirmKey, pendingRoundTrip, readPendingVerdict } from '../core/input-required.ts';\nimport {\n isPathInsideDirectory,\n isSamePath,\n respellSpellingOnlyTarget,\n unicodeMatchKey,\n} from '../core/path-utils.ts';\nimport { defaultFalseBoolean, RequiredPath } from '../core/schema.ts';\nimport { PARALLEL_CONCURRENCY } from '../core/util.ts';\nimport type { ToolCtx } from './define.ts';\nimport { defineTool } from './define.ts';\n\nconst MoveItemSchema = z.strictObject({\n source: RequiredPath.describe('Path of the file or directory to move or copy'),\n destination: RequiredPath.describe('Destination path'),\n});\n\ninterface MoveItemResult {\n from: string;\n to: string;\n}\n\nconst MoveInputSchema = z.strictObject({\n moves: z.array(MoveItemSchema).min(1).max(100).describe('Operations to perform (max 100)'),\n copy: defaultFalseBoolean('Copy instead of move; sources are left in place'),\n overwrite: defaultFalseBoolean(\n 'Copy mode only: overwrite existing destinations without confirmation',\n ),\n});\n\ninterface MoveFailureItem {\n source: string;\n destination: string;\n error: Problem;\n}\n\ninterface MoveOutput {\n moves: MoveItemResult[];\n failures?: MoveFailureItem[];\n skipped?: string[];\n}\n\ntype PairOp = 'move' | 'copy';\n\nconst VERB: Readonly<Record<PairOp, string>> = { move: 'Move', copy: 'Copy' };\n\n/** A move or copy pre-checked up to the point a confirmation decision is needed. */\ninterface TransferPlan {\n pair: { source: string; destination: string };\n /**\n * The path the mutation runs on: the link itself for a move, the resolved\n * target for a copy. Every collision check uses the resolved source instead\n * — see {@link validateTransferSource}.\n */\n opSource: string;\n /** Symlink-resolved source, for cross-entry overlap detection (opSource may be the link). */\n resolvedSource: string;\n validDest: string;\n /**\n * A rename that only changes the destination's case or Unicode spelling\n * (#101). Move must perform it —\n * it is real work — so it is exempt from the destination-exists checks that\n * would otherwise read the source itself as an existing destination. Never\n * true for a copy, which treats such a target as a self-copy and skips.\n */\n isSpellingOnlyRename: boolean;\n /** Whether the destination existed when planned (drives the TOCTOU guard). */\n destExistedOriginally: boolean;\n /** Destination exists and is not a self or spelling-only target → needs overwrite confirmation. */\n pending: boolean;\n}\n\n/**\n * Phase 1 (no mutation): validate source and destination, reject self-targets\n * and transfers into a subdirectory of the source, and stat the destination to\n * decide whether this pair needs an overwrite confirmation. No `mkdir` happens\n * here — the old flow created the destination's parent before asking, which\n * mutated the filesystem before a confirmation; that now waits for phase 2 (R14).\n *\n * `overwrite` is copy-only — move has no confirmation bypass — so a move always\n * passes false.\n */\nasync function planTransfer(\n op: PairOp,\n pair: { source: string; destination: string },\n fs: ToolCtx['fs'],\n overwrite: boolean,\n signal: AbortSignal | undefined,\n): Promise<TransferPlanResult> {\n let realSource: string;\n let opSource: string;\n let validDest: string;\n let isDirectory: boolean;\n try {\n const source = await validateTransferSource(op, pair.source, fs, signal);\n ({ realSource, opSource, isDirectory } = source);\n validDest = await fs.pathGuard.validatePathForWrite(pair.destination);\n } catch (error) {\n return { status: 'fail', failure: pairFailure(pair, error) };\n }\n\n // Comparisons run on the resolved source; only the fs call in phase 2 uses\n // opSource. validatePathForWrite resolves the destination through a symlink\n // too, so both sides of every check must be resolved to match.\n const resolvedSource = resolve(realSource);\n let resolvedDest = resolve(validDest);\n\n // The entry the destination names, without following a final link. When it\n // is the source's own entry in another case or Unicode spelling, this is a\n // rename of that entry, re-spelled with the requested basename: of the link\n // itself when the source is a link, which validatePathForWrite resolved to\n // its target. Onto its target, in any spelling, a link is a self-move: a\n // rename would replace that file with the link.\n const destEntry = await fs.pathGuard\n .validatePathForDelete(pair.destination)\n .catch(() => undefined);\n const sameEntry = destEntry !== undefined && isSamePath(destEntry, opSource);\n if (sameEntry) {\n validDest = respellSpellingOnlyTarget(pair.destination, opSource);\n resolvedDest = resolve(validDest);\n }\n\n // On the final destination spelling: a re-spelled directory moves its\n // entries in or out of a spelling-specific allow or deny rule.\n if (!(op === 'move' && opSource !== realSource) && isDirectory) {\n try {\n await assertTreeHasNoProtectedEntries(op, realSource, pair.source, validDest, fs, signal);\n } catch (error) {\n return { status: 'fail', failure: pairFailure(pair, error) };\n }\n }\n\n const isSelf = sameEntry ? resolvedDest === resolve(opSource) : resolvedSource === resolvedDest;\n // Only a destination naming the source's own entry is renamed in place:\n // onto a separate `a\\u202fb.txt`, a move of `a\\u00a0b.txt` is an overwrite.\n const isSpellingOnlyRename = !isSelf && (sameEntry || isSamePath(resolvedSource, resolvedDest));\n\n // A copy onto a case-only or Unicode-only variant of its own source is the\n // same file, so it is a no-op; the same rename is real work for move and\n // proceeds.\n if (isSelf || (isSpellingOnlyRename && op === 'copy')) {\n return { status: 'noop' };\n }\n\n if (!isSpellingOnlyRename && isPathInsideDirectory(resolvedSource, resolvedDest)) {\n return {\n status: 'fail',\n failure: pairFailure(\n pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `Cannot ${op} a directory into its own subdirectory`,\n pair.source,\n ),\n ),\n };\n }\n\n const destExistedOriginally = !isSpellingOnlyRename && (await destExists(fs, validDest, op));\n const pending = destExistedOriginally && !overwrite;\n\n return {\n status: 'plan',\n plan: {\n pair,\n opSource,\n resolvedSource,\n validDest,\n isSpellingOnlyRename,\n destExistedOriginally,\n pending,\n },\n };\n}\n\n/**\n * Phase 2 (mutation): confirm an overwrite if needed, TOCTOU-check the\n * destination, create its parent, then rename (with cross-device fallback) or\n * copy. A declined/missing confirmation throws `CANCELLED`, collected as a\n * per-pair failure by the caller. A destination that appeared between plan and\n * mutation (created during the confirmation gap) also fails closed.\n */\nasync function executeTransfer(\n op: PairOp,\n plan: TransferPlan,\n ctx: Pick<ToolCtx, 'fs' | 'signal' | 'inputResponses' | 'droppedInputResponseKeys' | 'log'>,\n pendingSorted: readonly string[],\n): Promise<TransferExecResult> {\n if (plan.pending) {\n const verdict = readPendingVerdict(ctx, pendingSorted, plan.validDest, 'overwrite');\n if (verdict === 'skipped') {\n return { skipped: plan.pair.destination };\n }\n if (verdict !== 'accepted') {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: overwrite of \"${plan.pair.destination}\" was ${verdict.refused}`,\n plan.pair.destination,\n );\n }\n }\n\n // TOCTOU check before any mutation: a destination that did not exist when\n // planned but exists now was created during the confirmation gap.\n if (\n !plan.isSpellingOnlyRename &&\n !plan.destExistedOriginally &&\n (await destExists(ctx.fs, plan.validDest, op))\n ) {\n throw new FsError(\n ErrorCode.CANCELLED,\n `${VERB[op]} cancelled: destination \"${plan.pair.destination}\" was created during confirmation.`,\n plan.pair.destination,\n );\n }\n\n await ctx.fs.mkdir(dirname(plan.validDest), { recursive: true });\n\n if (op === 'move') {\n await performRenameWithFallback(plan.opSource, plan.validDest, ctx.fs, plan.pair.source);\n ctx.log('info', `move: ${plan.pair.source} -> ${plan.pair.destination}`, 'move');\n } else {\n await ctx.fs.cp(plan.opSource, plan.validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n force: true,\n });\n }\n return { value: { from: plan.opSource, to: plan.validDest } };\n}\n\ntype TransferPlanResult =\n | { status: 'fail'; failure: MoveFailureItem }\n | { status: 'noop' }\n | { status: 'plan'; plan: TransferPlan };\n\n/** A discriminated union, so the fold below narrows without a generic cast. */\ntype TransferExecResult = { readonly skipped: string } | { readonly value: MoveItemResult };\n\nfunction pairFailure(\n pair: { source: string; destination: string },\n error: unknown,\n): MoveFailureItem {\n return {\n source: pair.source,\n destination: pair.destination,\n error: Problem.fromUnknown(error, ErrorCode.UNKNOWN, pair.source),\n };\n}\n\n/**\n * The source→destination sibling of `runOverPaths`: plan every pair, fail closed\n * on a duplicated destination, round-trip the overwrite confirmations as one\n * set, then execute what survived in parallel. `copy` and `move` differ only in\n * their `plan` and `execute` callbacks.\n */\nasync function runTransfers(\n items: readonly z.infer<typeof MoveItemSchema>[],\n ctx: ToolCtx,\n op: PairOp,\n overwrite: boolean,\n): Promise<\n | { results: MoveItemResult[]; skipped: string[]; failures: MoveFailureItem[] }\n | InputRequiredResult\n> {\n const { results: planned, errors: planErrors } = await processInParallel(\n items,\n (pair) => planTransfer(op, pair, ctx.fs, overwrite, ctx.signal),\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n // plan callbacks are fail-closed (they return { status: 'fail' }, never\n // throw), so planErrors should always be empty. Surface the first rather\n // than silently dropping the item if a future plan callback violates that.\n const firstPlanError = planErrors[0];\n if (firstPlanError) throw firstPlanError.error;\n\n const failures: MoveFailureItem[] = [];\n const candidates: TransferPlan[] = [];\n for (const { value } of planned) {\n if (value.status === 'fail') failures.push(value.failure);\n else if (value.status === 'plan') candidates.push(value.plan);\n // 'noop' (self-copy / self-move) is silently skipped.\n }\n\n // Two sources targeting the same destination in one batch would otherwise\n // collapse to a single shared overwrite confirmation and let the second\n // one silently clobber the first's freshly-written content. Fail closed:\n // only the first plan per destination proceeds; later ones targeting the\n // same destination are reported as a per-item failure.\n const seenDest = new Set<string>();\n const ready: TransferPlan[] = [];\n for (const candidate of candidates) {\n // Unicode-folded too: two spellings of one missing name land on one file\n // once the first move creates it (#101).\n const destKey = unicodeMatchKey(candidate.validDest);\n if (seenDest.has(destKey)) {\n failures.push(\n pairFailure(\n candidate.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${VERB[op]} cancelled: another entry in this batch already targets destination \"${candidate.pair.destination}\"`,\n candidate.pair.destination,\n ),\n ),\n );\n continue;\n }\n seenDest.add(destKey);\n ready.push(candidate);\n }\n\n // One entry's (resolved) source overlapping another's destination means the\n // two race and a file is lost: a swap [A->B, B->A] or a chain [B->C, A->B].\n // Destination subtrees must also be disjoint: two recursive copies otherwise\n // race over shared descendants even when their destination paths differ.\n // Refuse both sides so the survivors are independent and can run concurrently.\n // isPathInsideDirectory covers equality and containment, case-folded; the\n // keys fold Unicode spelling too, parents included. Two moves compare the\n // entries they rename (opSource), not where a link points, and unfolded:\n // validated sources are on-disk names, so two that fold alike are distinct.\n const touches = (x: string, y: string): boolean => {\n const [keyX, keyY] = [unicodeMatchKey(x), unicodeMatchKey(y)];\n return isPathInsideDirectory(keyX, keyY) || isPathInsideDirectory(keyY, keyX);\n };\n const overlapping = new Set<TransferPlan>();\n for (const [i, a] of ready.entries()) {\n for (const b of ready.slice(i + 1)) {\n if (\n (op === 'move' &&\n (isPathInsideDirectory(a.opSource, b.opSource) ||\n isPathInsideDirectory(b.opSource, a.opSource))) ||\n touches(a.resolvedSource, b.validDest) ||\n touches(b.resolvedSource, a.validDest) ||\n touches(a.validDest, b.validDest)\n ) {\n overlapping.add(a);\n overlapping.add(b);\n }\n }\n }\n const safe: TransferPlan[] = [];\n for (const plan of ready) {\n if (!overlapping.has(plan)) {\n safe.push(plan);\n continue;\n }\n failures.push(\n pairFailure(\n plan.pair,\n new FsError(\n ErrorCode.INVALID_INPUT,\n `${VERB[op]} cancelled: \"${plan.pair.source}\" or \"${plan.pair.destination}\" overlaps another entry's source or destination in this batch. Issue dependent moves one at a time.`,\n plan.pair.source,\n ),\n ),\n );\n }\n\n const pendingSorted = safe\n .filter((p) => p.pending)\n .map((p) => p.validDest)\n .sort();\n if (pendingSorted.length > 0) {\n // Round 1 returns input_required; a retry whose verified state does not\n // bind this overwrite set throws (R9) via `pendingRoundTrip`.\n const round = await pendingRoundTrip(ctx, {\n op,\n pending: pendingSorted,\n buildInputs: (dests) =>\n dests.map((dest, i) => ({\n key: confirmKey(i),\n message:\n op === 'move'\n ? `\"${dest}\" already exists. Overwrite it?`\n : `Destination \"${dest}\" already exists. Overwrite it?`,\n choices: ['overwrite', 'skip'],\n })),\n });\n if (round !== undefined) return round;\n }\n\n const total = safe.length;\n let completed = 0;\n const tick = (): void => {\n completed += 1;\n ctx.onProgress({ current: completed, total });\n };\n const { results: execResults, errors: execErrors } = await processInParallel(\n safe,\n async (readyPlan) => {\n try {\n return await executeTransfer(op, readyPlan, ctx, pendingSorted);\n } finally {\n tick();\n }\n },\n PARALLEL_CONCURRENCY,\n ctx.signal,\n );\n\n for (const { error, index } of execErrors) {\n rethrowIfAborted(error);\n const failed = safe[index];\n if (failed) failures.push(pairFailure(failed.pair, error));\n }\n\n const results: MoveItemResult[] = [];\n const skipped: string[] = [];\n for (const { value } of execResults) {\n if ('skipped' in value) skipped.push(value.skipped);\n else results.push(value.value);\n }\n\n return { results, skipped, failures };\n}\n\nfunction buildSummary(\n verb: 'move' | 'copy',\n results: readonly MoveItemResult[],\n failures: readonly MoveFailureItem[],\n skipped: readonly string[],\n): string {\n const failCount = failures.length;\n // Name each pair. \"move: 2 items\" left the caller to open structuredContent\n // to learn which two, and a partial failure was unreadable without it.\n // Skipped destinations are an outcome the user chose, not a failure — name\n // them, or an all-skipped call reads as \"copy: nothing\".\n const tokens = [\n ...results.map((r) => `${pathLabel(r.from)} → ${pathLabel(r.to)}`),\n ...skipped.map((dest) => `${pathLabel(dest)} SKIPPED`),\n ];\n const parts = [`${verb}: ${joinRoster(tokens) || 'nothing'}`];\n if (failCount > 0) parts.push(`${String(failCount)} failed`);\n return parts.join(' · ');\n}\n\ninterface TransferSource {\n /** Symlink resolved — the identity used for every same-target comparison. */\n realSource: string;\n /** The path the mutation runs on: symlink preserved for move, resolved for copy. */\n opSource: string;\n /** Whether the resolved source is a directory (from the same guarded stat). */\n isDirectory: boolean;\n}\n\n/**\n * A move needs two views of its source. Renaming must operate on the link\n * itself, or moving a symlink would rename the file it points at and leave the\n * link dangling. Every collision check must instead compare the resolved\n * target, or a link moved onto itself (or onto its own target) reads as a real\n * move and renames the link over that target, destroying it. A copy reads\n * through the link, so both views are the resolved one.\n */\nasync function validateTransferSource(\n op: PairOp,\n source: string,\n fs: ToolCtx['fs'],\n signal: AbortSignal | undefined,\n): Promise<TransferSource> {\n try {\n if (op === 'move') {\n const opSource = await fs.pathGuard.validatePathForDelete(source);\n try {\n const { stats, validPath: realSource } = await fs.stat(source, { signal });\n return { realSource, opSource, isDirectory: stats.isDirectory() };\n } catch (err) {\n if (!isFsError(err) || err.code !== ErrorCode.NOT_FOUND) throw err;\n const { stats } = await fs.lstat(source, { signal });\n if (!stats.isSymbolicLink()) throw err;\n return { realSource: opSource, opSource, isDirectory: false };\n }\n }\n const { stats, validPath: realSource } = await fs.stat(source, { signal });\n return { realSource, opSource: realSource, isDirectory: stats.isDirectory() };\n } catch (error) {\n if (isFsError(error) || signal?.aborted) throw error;\n throw new FsError(ErrorCode.ACCESS_DENIED, `${VERB[op]} failed for ${source}`, source);\n }\n}\n\n/**\n * A directory transfer may proceed only when every entry keeps the same\n * sensitive-file status before and after relocation. Path-based rules\n * (`secrets/**`, `.aws/credentials`) follow the path, not the file, so refuse\n * any entry that would leave or enter the protected set. Walk the tree once\n * before any mutation and fail the pair closed if anything changes status.\n * Name-based rules need no help — they match after the move too.\n */\nasync function assertTreeHasNoProtectedEntries(\n op: PairOp,\n realSource: string,\n requestedSource: string,\n validDest: string,\n fs: ToolCtx['fs'],\n signal: AbortSignal | undefined,\n): Promise<void> {\n let protectedCount = 0;\n const entries = globEntries({\n cwd: realSource,\n pattern: '**/*',\n includeHidden: true,\n skipIgnored: false,\n onlyFiles: false,\n ...(signal ? { signal } : {}),\n });\n try {\n for await (const entry of entries) {\n signal?.throwIfAborted();\n const entryRelativePath = relative(realSource, entry.path);\n const requestedSourcePath = resolve(requestedSource, entryRelativePath);\n const destinationPath = resolve(validDest, entryRelativePath);\n const protectedBefore =\n fs.pathGuard.isSensitive(entry.path) || fs.pathGuard.isSensitive(requestedSourcePath);\n const protectedAfter = fs.pathGuard.isSensitive(destinationPath);\n if (protectedBefore !== protectedAfter) {\n protectedCount++;\n break;\n }\n }\n } catch (error) {\n rethrowIfAborted(error);\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `${VERB[op]} refused: the directory could not be fully inspected for protected entries`,\n requestedSource,\n error,\n );\n }\n if (protectedCount > 0) {\n throw new FsError(\n ErrorCode.ACCESS_DENIED,\n `${VERB[op]} refused: the directory contains protected entries matching the sensitive-file policy; move or copy individual files instead`,\n requestedSource,\n );\n }\n}\n\nexport async function performRenameWithFallback(\n validSource: string,\n validDest: string,\n fsOps: Pick<GuardedFileSystem, 'rename' | 'cp' | 'rm'>,\n originalSource: string,\n): Promise<void> {\n try {\n await fsOps.rename(validSource, validDest);\n } catch (error: unknown) {\n rethrowIfAborted(error);\n\n if (!isNodeError(error) || error.code !== 'EXDEV') {\n throw error;\n }\n\n // EXDEV: cross-device rename. Copy then remove, preserving symlinks and\n // timestamps so link semantics survive the move.\n let copied = false;\n try {\n await fsOps.cp(validSource, validDest, {\n recursive: true,\n verbatimSymlinks: true,\n preserveTimestamps: true,\n });\n copied = true;\n await fsOps.rm(validSource, { recursive: true, force: true });\n } catch (copyOrRemoveError) {\n rethrowIfAborted(copyOrRemoveError);\n if (copied) {\n // cp succeeded but rm failed: the destination already holds a complete\n // copy and the source remains. Surface the rm error as the cause so the\n // caller can recover (clean up the duplicate) instead of a silent generic\n // failure that hides the partial completion.\n const baseProblem = Problem.fromUnknown(\n copyOrRemoveError,\n ErrorCode.UNKNOWN,\n originalSource,\n );\n throw new FsError(\n baseProblem.code,\n `Cross-device move of ${originalSource}: copy succeeded but source removal failed (destination holds a copy): ${baseProblem.message}`,\n originalSource,\n copyOrRemoveError,\n );\n }\n throw copyOrRemoveError;\n }\n }\n}\n\nexport const MOVE = defineTool<typeof MoveInputSchema, MoveOutput>({\n name: 'move',\n title: 'Move or Copy Files',\n description:\n 'Move, rename, or copy files and directories, like mv or cp -r. ' +\n 'Each destination is the full new path, not a folder to move into; missing parent directories are created.',\n input: MoveInputSchema,\n readOnlyHint: false,\n progress: (args) => {\n const label = args.copy ? 'Copy' : 'Move';\n if (args.moves.length === 1) {\n const move = args.moves[0];\n return {\n label,\n subject: `${basename(move?.source ?? '')} → ${basename(move?.destination ?? '')}`,\n };\n }\n return { label, subject: `${String(args.moves.length)} files` };\n },\n mapPaths: (args, fn) => ({\n ...args,\n moves: args.moves.map((m) => ({ ...m, source: fn(m.source), destination: fn(m.destination) })),\n }),\n run: async (args, ctx) => {\n const op: PairOp = args.copy ? 'copy' : 'move';\n // `overwrite` is copy-only; move has no confirmation bypass.\n const outcome = await runTransfers(args.moves, ctx, op, args.copy && args.overwrite);\n // input_required is a return value, not a completed call: surface it\n // verbatim so the executor short-circuits before building a CallToolResult.\n if (isInputRequiredResult(outcome)) return outcome;\n const { results, skipped, failures } = outcome;\n return {\n structured: {\n moves: results,\n ...(failures.length > 0 ? { failures } : {}),\n ...(skipped.length > 0 ? { skipped } : {}),\n },\n text: buildSummary(op, results, failures, skipped),\n // Every requested pair failed: no move, no copy, no user-chosen skip - the\n // call did nothing. A skip is work the caller asked for, so it counts.\n isError: failures.length > 0 && results.length + skipped.length === 0,\n };\n },\n});\n"]}
@@ -41,7 +41,7 @@ export const PATCH = defineTool({
41
41
  label: args.dryRun ? 'Patch [dry run]' : 'Patch',
42
42
  subject: basename(args.path),
43
43
  }),
44
- accessPaths: (args) => [args.path],
44
+ mapPaths: (args, fn) => ({ ...args, path: fn(args.path) }),
45
45
  run: async (args, ctx) =>
46
46
  // A dry run never writes, so it needs no lock.
47
47
  args.dryRun
@@ -106,21 +106,28 @@ async function applyPatchOnce(args, ctx) {
106
106
  // `modified` is read from a post-write stat and is advisory: under a concurrent
107
107
  // writer it may reflect that writer's mtime while `size`/content come from this
108
108
  // patch's atomic write. The file content itself is always consistent.
109
- const fileStats = args.dryRun
110
- ? stats
111
- : (await ctx.fs.stat(args.path, { signal: ctx.signal })).stats;
109
+ let modified = stats.mtime.toISOString();
110
+ if (!args.dryRun) {
111
+ try {
112
+ modified = (await ctx.fs.stat(validPath)).stats.mtime.toISOString();
113
+ }
114
+ catch {
115
+ modified = new Date().toISOString();
116
+ }
117
+ }
112
118
  const meta = buildWrittenFileMeta({
113
119
  validPath,
114
120
  content: patched,
115
121
  dryRun: args.dryRun,
116
- lastModified: fileStats.mtime.toISOString(),
122
+ lastModified: modified,
123
+ transport: ctx.transport,
117
124
  });
118
125
  const { resourceLink, ...fileMeta } = meta;
119
126
  return {
120
127
  structured: {
121
128
  path: validPath,
122
129
  ...fileMeta,
123
- modified: fileStats.mtime.toISOString(),
130
+ modified,
124
131
  linesAdded,
125
132
  linesRemoved,
126
133
  ...(args.dryRun ? { diff: args.diff } : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"patch.js","sourceRoot":"","sources":["../../src/tools/patch.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\nimport { applyPatch, parsePatch } from 'diff';\n\nimport { ErrorCode, FsError } from '../core/errors.ts';\nimport { buildWrittenFileMeta, type WrittenFileMeta } from '../core/file-uri.ts';\nimport { countLines } from '../core/read.ts';\nimport { defaultFalseBoolean, RequiredPath } from '../core/schema.ts';\nimport { defineTool, type ToolCtx } from './define.ts';\n\nconst PatchInputSchema = z.strictObject({\n path: RequiredPath.describe('File to apply the diff to'),\n diff: z\n .string()\n .min(1)\n .describe('Single-file unified diff to apply (as produced by the diff tool or edit dry-run)'),\n dryRun: defaultFalseBoolean('Preview the result without writing (default: false)'),\n});\n\ntype PatchOutput = Omit<WrittenFileMeta, 'resourceLink'> & {\n path: string;\n modified: string;\n linesAdded: number;\n linesRemoved: number;\n diff?: string;\n};\n\n// Hunk lines are the raw unified lines with their +/-/ leading prefix (no\n// +++/--- file headers inside hunks), so the first char is a reliable marker.\nfunction countAddedRemoved(parsedPatch: ReturnType<typeof parsePatch>[number]): {\n linesAdded: number;\n linesRemoved: number;\n} {\n let linesAdded = 0;\n let linesRemoved = 0;\n for (const hunk of parsedPatch.hunks) {\n for (const line of hunk.lines) {\n if (line.startsWith('+')) linesAdded += 1;\n else if (line.startsWith('-')) linesRemoved += 1;\n }\n }\n return { linesAdded, linesRemoved };\n}\n\nexport const PATCH = defineTool<typeof PatchInputSchema, PatchOutput>({\n name: 'patch',\n title: 'Patch',\n description:\n 'Apply a single-file unified diff to one existing text file, like git apply. ' +\n 'Nothing is written unless every hunk applies in file order with exactly matching context; ' +\n 'hunks are found by their context, so start line numbers may be off.',\n input: PatchInputSchema,\n readOnlyHint: false,\n progress: (args) => ({\n label: args.dryRun ? 'Patch [dry run]' : 'Patch',\n subject: basename(args.path),\n }),\n accessPaths: (args) => [args.path],\n run: async (args, ctx) =>\n // A dry run never writes, so it needs no lock.\n args.dryRun\n ? applyPatchOnce(args, ctx)\n : ctx.fs.lockForWrite(args.path, () => applyPatchOnce(args, ctx)),\n});\n\nasync function applyPatchOnce(args: z.infer<typeof PatchInputSchema>, ctx: ToolCtx) {\n const { validPath, content, stats } = await ctx.fs.readEditableText(args.path, {\n signal: ctx.signal,\n tool: 'patch',\n });\n\n // patch is single-file only: a multi-file unified diff (parsePatch length > 1)\n // is rejected. Multi-file application needs per-file PathGuard-resolved loaders\n // and is deferred (out of scope).\n const parsed = parsePatch(args.diff);\n if (parsed.length !== 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `patch accepts a single-file unified diff; received ${parsed.length} file(s)`,\n args.path,\n );\n }\n const parsedPatch = parsed[0];\n if (!parsedPatch || parsedPatch.hunks.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch accepts a single-file unified diff with at least one hunk; received an empty patch',\n args.path,\n );\n }\n // jsdiff tries each hunk at its header line before bounding the search by\n // the previous hunk, so a header pointing back into text an earlier hunk\n // already consumed applies there and emits that region twice. Start each\n // hunk no earlier than where the previous hunk's changes end; its trailing\n // context may still overlap, as jsdiff allows.\n let floor = 0;\n const hunks = parsedPatch.hunks.map((hunk) => {\n const oldStart = Math.max(hunk.oldStart, floor);\n let trailingContext = 0;\n for (const line of hunk.lines) {\n if (line === '' || line.startsWith(' ')) trailingContext++;\n else if (line.startsWith('+') || line.startsWith('-')) trailingContext = 0;\n }\n floor = oldStart + hunk.oldLines - trailingContext;\n return { ...hunk, oldStart };\n });\n // applyPatch returns false (strict) on hunk mismatch — not a falsy empty string,\n // so `=== false` distinguishes a no-op patch ('') from a failed one.\n const patched = applyPatch(content, { ...parsedPatch, hunks });\n if (patched === false) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch did not apply cleanly: hunk context does not match file content (hunks must be in file order)',\n args.path,\n );\n }\n\n const { linesAdded, linesRemoved } = countAddedRemoved(parsedPatch);\n // Backstop for placements the clamp cannot see, such as zero-context hunks\n // whose headers point past the end of the file: refuse rather than write.\n if (countLines(patched) !== countLines(content) + linesAdded - linesRemoved) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch would duplicate or drop lines: a hunk overlaps an earlier one or starts past the end of the file',\n args.path,\n );\n }\n // The patched file used to come back whole in the text block, so a one-line\n // hunk against a 5000-line file returned 5000 lines. Every other write tool\n // answers with a summary; the result is reachable via `resourceUri` or a\n // follow-up `read` when the caller actually wants the bytes.\n const summaryText = `patch: ${basename(args.path)} +${String(linesAdded)} -${String(linesRemoved)}`;\n\n if (!args.dryRun) {\n await ctx.fs.writeFile(args.path, patched, { signal: ctx.signal });\n ctx.log('info', `patch: ${args.path} (+${linesAdded}/-${linesRemoved})`, 'patch');\n }\n\n // `modified` is read from a post-write stat and is advisory: under a concurrent\n // writer it may reflect that writer's mtime while `size`/content come from this\n // patch's atomic write. The file content itself is always consistent.\n const fileStats = args.dryRun\n ? stats\n : (await ctx.fs.stat(args.path, { signal: ctx.signal })).stats;\n const meta = buildWrittenFileMeta({\n validPath,\n content: patched,\n dryRun: args.dryRun,\n lastModified: fileStats.mtime.toISOString(),\n });\n const { resourceLink, ...fileMeta } = meta;\n return {\n structured: {\n path: validPath,\n ...fileMeta,\n modified: fileStats.mtime.toISOString(),\n linesAdded,\n linesRemoved,\n ...(args.dryRun ? { diff: args.diff } : {}),\n },\n // A dry run exists to be previewed, so it keeps returning the patched\n // text; only the write path summarizes, where the bytes are on disk.\n text: args.dryRun ? patched : summaryText,\n ...(resourceLink !== undefined ? { resources: [resourceLink] } : {}),\n };\n}\n"]}
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../../src/tools/patch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAwB,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAEvD,MAAM,gBAAgB,GAAG,CAAC,CAAC,YAAY,CAAC;IACtC,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,2BAA2B,CAAC;IACxD,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kFAAkF,CAAC;IAC/F,MAAM,EAAE,mBAAmB,CAAC,qDAAqD,CAAC;CACnF,CAAC,CAAC;AAUH,0EAA0E;AAC1E,8EAA8E;AAC9E,SAAS,iBAAiB,CAAC,WAAkD;IAI3E,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,UAAU,IAAI,CAAC,CAAC;iBACrC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,YAAY,IAAI,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAAuC;IACpE,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,OAAO;IACd,WAAW,EACT,8EAA8E;QAC9E,4FAA4F;QAC5F,qEAAqE;IACvE,KAAK,EAAE,gBAAgB;IACvB,YAAY,EAAE,KAAK;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACnB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO;QAChD,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;KAC7B,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC1D,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;IACvB,+CAA+C;IAC/C,IAAI,CAAC,MAAM;QACT,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC;QAC3B,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;CACtE,CAAC,CAAC;AAEH,KAAK,UAAU,cAAc,CAAC,IAAsC,EAAE,GAAY;IAChF,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE;QAC7E,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,OAAO;KACd,CAAC,CAAC;IAEH,+EAA+E;IAC/E,gFAAgF;IAChF,kCAAkC;IAClC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,OAAO,CACf,SAAS,CAAC,aAAa,EACvB,sDAAsD,MAAM,CAAC,MAAM,UAAU,EAC7E,IAAI,CAAC,IAAI,CACV,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,OAAO,CACf,SAAS,CAAC,aAAa,EACvB,0FAA0F,EAC1F,IAAI,CAAC,IAAI,CACV,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,yEAAyE;IACzE,yEAAyE;IACzE,2EAA2E;IAC3E,+CAA+C;IAC/C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAChD,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,eAAe,EAAE,CAAC;iBACtD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,eAAe,GAAG,CAAC,CAAC;QAC7E,CAAC;QACD,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC,QAAQ,GAAG,eAAe,CAAC;QACnD,OAAO,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IACH,iFAAiF;IACjF,qEAAqE;IACrE,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,GAAG,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,OAAO,CACf,SAAS,CAAC,aAAa,EACvB,qGAAqG,EACrG,IAAI,CAAC,IAAI,CACV,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpE,2EAA2E;IAC3E,0EAA0E;IAC1E,IAAI,UAAU,CAAC,OAAO,CAAC,KAAK,UAAU,CAAC,OAAO,CAAC,GAAG,UAAU,GAAG,YAAY,EAAE,CAAC;QAC5E,MAAM,IAAI,OAAO,CACf,SAAS,CAAC,aAAa,EACvB,wGAAwG,EACxG,IAAI,CAAC,IAAI,CACV,CAAC;IACJ,CAAC;IACD,4EAA4E;IAC5E,4EAA4E;IAC5E,yEAAyE;IACzE,6DAA6D;IAC7D,MAAM,WAAW,GAAG,UAAU,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;IAEpG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,IAAI,CAAC,IAAI,MAAM,UAAU,KAAK,YAAY,GAAG,EAAE,OAAO,CAAC,CAAC;IACpF,CAAC;IAED,gFAAgF;IAChF,gFAAgF;IAChF,sEAAsE;IACtE,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IACzC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,oBAAoB,CAAC;QAChC,SAAS;QACT,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY,EAAE,QAAQ;QACtB,SAAS,EAAE,GAAG,CAAC,SAAS;KACzB,CAAC,CAAC;IACH,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC3C,OAAO;QACL,UAAU,EAAE;YACV,IAAI,EAAE,SAAS;YACf,GAAG,QAAQ;YACX,QAAQ;YACR,UAAU;YACV,YAAY;YACZ,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5C;QACD,sEAAsE;QACtE,qEAAqE;QACrE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW;QACzC,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC","sourcesContent":["import { basename } from 'node:path';\n\nimport * as z from 'zod/v4';\nimport { applyPatch, parsePatch } from 'diff';\n\nimport { ErrorCode, FsError } from '../core/errors.ts';\nimport { buildWrittenFileMeta, type WrittenFileMeta } from '../core/file-uri.ts';\nimport { countLines } from '../core/read.ts';\nimport { defaultFalseBoolean, RequiredPath } from '../core/schema.ts';\nimport { defineTool, type ToolCtx } from './define.ts';\n\nconst PatchInputSchema = z.strictObject({\n path: RequiredPath.describe('File to apply the diff to'),\n diff: z\n .string()\n .min(1)\n .describe('Single-file unified diff to apply (as produced by the diff tool or edit dry-run)'),\n dryRun: defaultFalseBoolean('Preview the result without writing (default: false)'),\n});\n\ntype PatchOutput = Omit<WrittenFileMeta, 'resourceLink'> & {\n path: string;\n modified: string;\n linesAdded: number;\n linesRemoved: number;\n diff?: string;\n};\n\n// Hunk lines are the raw unified lines with their +/-/ leading prefix (no\n// +++/--- file headers inside hunks), so the first char is a reliable marker.\nfunction countAddedRemoved(parsedPatch: ReturnType<typeof parsePatch>[number]): {\n linesAdded: number;\n linesRemoved: number;\n} {\n let linesAdded = 0;\n let linesRemoved = 0;\n for (const hunk of parsedPatch.hunks) {\n for (const line of hunk.lines) {\n if (line.startsWith('+')) linesAdded += 1;\n else if (line.startsWith('-')) linesRemoved += 1;\n }\n }\n return { linesAdded, linesRemoved };\n}\n\nexport const PATCH = defineTool<typeof PatchInputSchema, PatchOutput>({\n name: 'patch',\n title: 'Patch',\n description:\n 'Apply a single-file unified diff to one existing text file, like git apply. ' +\n 'Nothing is written unless every hunk applies in file order with exactly matching context; ' +\n 'hunks are found by their context, so start line numbers may be off.',\n input: PatchInputSchema,\n readOnlyHint: false,\n progress: (args) => ({\n label: args.dryRun ? 'Patch [dry run]' : 'Patch',\n subject: basename(args.path),\n }),\n mapPaths: (args, fn) => ({ ...args, path: fn(args.path) }),\n run: async (args, ctx) =>\n // A dry run never writes, so it needs no lock.\n args.dryRun\n ? applyPatchOnce(args, ctx)\n : ctx.fs.lockForWrite(args.path, () => applyPatchOnce(args, ctx)),\n});\n\nasync function applyPatchOnce(args: z.infer<typeof PatchInputSchema>, ctx: ToolCtx) {\n const { validPath, content, stats } = await ctx.fs.readEditableText(args.path, {\n signal: ctx.signal,\n tool: 'patch',\n });\n\n // patch is single-file only: a multi-file unified diff (parsePatch length > 1)\n // is rejected. Multi-file application needs per-file PathGuard-resolved loaders\n // and is deferred (out of scope).\n const parsed = parsePatch(args.diff);\n if (parsed.length !== 1) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n `patch accepts a single-file unified diff; received ${parsed.length} file(s)`,\n args.path,\n );\n }\n const parsedPatch = parsed[0];\n if (!parsedPatch || parsedPatch.hunks.length === 0) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch accepts a single-file unified diff with at least one hunk; received an empty patch',\n args.path,\n );\n }\n // jsdiff tries each hunk at its header line before bounding the search by\n // the previous hunk, so a header pointing back into text an earlier hunk\n // already consumed applies there and emits that region twice. Start each\n // hunk no earlier than where the previous hunk's changes end; its trailing\n // context may still overlap, as jsdiff allows.\n let floor = 0;\n const hunks = parsedPatch.hunks.map((hunk) => {\n const oldStart = Math.max(hunk.oldStart, floor);\n let trailingContext = 0;\n for (const line of hunk.lines) {\n if (line === '' || line.startsWith(' ')) trailingContext++;\n else if (line.startsWith('+') || line.startsWith('-')) trailingContext = 0;\n }\n floor = oldStart + hunk.oldLines - trailingContext;\n return { ...hunk, oldStart };\n });\n // applyPatch returns false (strict) on hunk mismatch — not a falsy empty string,\n // so `=== false` distinguishes a no-op patch ('') from a failed one.\n const patched = applyPatch(content, { ...parsedPatch, hunks });\n if (patched === false) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch did not apply cleanly: hunk context does not match file content (hunks must be in file order)',\n args.path,\n );\n }\n\n const { linesAdded, linesRemoved } = countAddedRemoved(parsedPatch);\n // Backstop for placements the clamp cannot see, such as zero-context hunks\n // whose headers point past the end of the file: refuse rather than write.\n if (countLines(patched) !== countLines(content) + linesAdded - linesRemoved) {\n throw new FsError(\n ErrorCode.INVALID_INPUT,\n 'patch would duplicate or drop lines: a hunk overlaps an earlier one or starts past the end of the file',\n args.path,\n );\n }\n // The patched file used to come back whole in the text block, so a one-line\n // hunk against a 5000-line file returned 5000 lines. Every other write tool\n // answers with a summary; the result is reachable via `resourceUri` or a\n // follow-up `read` when the caller actually wants the bytes.\n const summaryText = `patch: ${basename(args.path)} +${String(linesAdded)} -${String(linesRemoved)}`;\n\n if (!args.dryRun) {\n await ctx.fs.writeFile(args.path, patched, { signal: ctx.signal });\n ctx.log('info', `patch: ${args.path} (+${linesAdded}/-${linesRemoved})`, 'patch');\n }\n\n // `modified` is read from a post-write stat and is advisory: under a concurrent\n // writer it may reflect that writer's mtime while `size`/content come from this\n // patch's atomic write. The file content itself is always consistent.\n let modified = stats.mtime.toISOString();\n if (!args.dryRun) {\n try {\n modified = (await ctx.fs.stat(validPath)).stats.mtime.toISOString();\n } catch {\n modified = new Date().toISOString();\n }\n }\n const meta = buildWrittenFileMeta({\n validPath,\n content: patched,\n dryRun: args.dryRun,\n lastModified: modified,\n transport: ctx.transport,\n });\n const { resourceLink, ...fileMeta } = meta;\n return {\n structured: {\n path: validPath,\n ...fileMeta,\n modified,\n linesAdded,\n linesRemoved,\n ...(args.dryRun ? { diff: args.diff } : {}),\n },\n // A dry run exists to be previewed, so it keeps returning the patched\n // text; only the write path summarizes, where the bytes are on disk.\n text: args.dryRun ? patched : summaryText,\n ...(resourceLink !== undefined ? { resources: [resourceLink] } : {}),\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAkUA,eAAO,MAAM,IAAI,mCAuHf,CAAC"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AA4ZA,eAAO,MAAM,IAAI,mCAgJf,CAAC"}