@carboncode/cli 0.1.0 → 0.1.2

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 (99) hide show
  1. package/README.md +15 -24
  2. package/README.zh-CN.md +13 -11
  3. package/dist/cli/{acp-35C4ME6Y.js → acp-6J54TVVC.js} +17 -16
  4. package/dist/cli/acp-6J54TVVC.js.map +1 -0
  5. package/dist/cli/{chat-A6UJDPGV.js → chat-636MFZ7W.js} +21 -21
  6. package/dist/cli/{chunk-JKGYMRX5.js → chunk-3N7FTZVE.js} +2 -2
  7. package/dist/cli/{chunk-4TVNJWMA.js → chunk-ACHQFKW2.js} +178 -18
  8. package/dist/cli/chunk-ACHQFKW2.js.map +1 -0
  9. package/dist/cli/{chunk-7L2WTRNU.js → chunk-ANVEA3RU.js} +2 -2
  10. package/dist/cli/{chunk-QVC75MR3.js → chunk-BXMMGFAL.js} +2 -2
  11. package/dist/cli/{chunk-UI66BH6D.js → chunk-COTWTQQZ.js} +2 -2
  12. package/dist/cli/{chunk-J5BYPUB5.js → chunk-CZCPIK5K.js} +1508 -1176
  13. package/dist/cli/chunk-CZCPIK5K.js.map +1 -0
  14. package/dist/cli/{chunk-XJ5SRLKK.js → chunk-D3ACJ6D5.js} +2 -2
  15. package/dist/cli/{chunk-WRN65TRD.js → chunk-DSQNSP7F.js} +2 -2
  16. package/dist/cli/{chunk-QJG7OF27.js → chunk-FKSYTVWZ.js} +27 -10
  17. package/dist/cli/chunk-FKSYTVWZ.js.map +1 -0
  18. package/dist/cli/{chunk-BSINVTTL.js → chunk-FXG7CSGY.js} +7 -7
  19. package/dist/cli/{chunk-4MQ3VURH.js → chunk-K43DXH3G.js} +52 -83
  20. package/dist/cli/chunk-K43DXH3G.js.map +1 -0
  21. package/dist/cli/{chunk-BSGCXZQN.js → chunk-LNU3CR7X.js} +2 -2
  22. package/dist/cli/{chunk-TH756VLN.js → chunk-MXUSER5C.js} +240 -191
  23. package/dist/cli/chunk-MXUSER5C.js.map +1 -0
  24. package/dist/cli/{chunk-3T6VBZCL.js → chunk-NQJYZKEU.js} +2 -2
  25. package/dist/cli/{chunk-IX6XI2RG.js → chunk-OB5XR5HG.js} +2 -2
  26. package/dist/cli/{chunk-ILJOIQ5W.js → chunk-OY5GGU6D.js} +2 -2
  27. package/dist/cli/{chunk-IAUOP25G.js → chunk-R677DIFU.js} +38 -22
  28. package/dist/cli/chunk-R677DIFU.js.map +1 -0
  29. package/dist/cli/{chunk-CPKCNHRR.js → chunk-RSQMO6CF.js} +5 -5
  30. package/dist/cli/{chunk-3OAR6NVL.js → chunk-RUPXIRNL.js} +2 -2
  31. package/dist/cli/{chunk-S2KIUQKQ.js → chunk-S4YD3N3X.js} +7 -6
  32. package/dist/cli/{chunk-S2KIUQKQ.js.map → chunk-S4YD3N3X.js.map} +1 -1
  33. package/dist/cli/{chunk-4IBIPQVB.js → chunk-T6SBUSG2.js} +3 -3
  34. package/dist/cli/{chunk-D5NFKRGO.js → chunk-UGPC4LPM.js} +2 -2
  35. package/dist/cli/{chunk-T5TQ4NDT.js → chunk-X4UJ6Q6M.js} +3 -3
  36. package/dist/cli/{code-4TUTAGO5.js → code-TBC3K5AZ.js} +24 -33
  37. package/dist/cli/code-TBC3K5AZ.js.map +1 -0
  38. package/dist/cli/{commands-KMOZEYCF.js → commands-HMQPRVNT.js} +4 -4
  39. package/dist/cli/{commit-DTFA56VQ.js → commit-WIY4B3X4.js} +3 -3
  40. package/dist/cli/{desktop-7N3MHNBD.js → desktop-MGOG3AWV.js} +17 -17
  41. package/dist/cli/{diff-E5OWTF4C.js → diff-57LRKCB7.js} +8 -8
  42. package/dist/cli/{doctor-IEJQRJMN.js → doctor-5FDRBIXE.js} +8 -8
  43. package/dist/cli/index.js +32 -32
  44. package/dist/cli/{mcp-PDI2PDLG.js → mcp-HJHTNRZF.js} +2 -2
  45. package/dist/cli/{mcp-browse-OSPXOFPZ.js → mcp-browse-C2PJRQBO.js} +2 -2
  46. package/dist/cli/{mcp-inspect-QRFVTHMF.js → mcp-inspect-JBFXV2II.js} +2 -2
  47. package/dist/cli/{prompt-3CDII3UO.js → prompt-U62OVZNY.js} +3 -3
  48. package/dist/cli/{replay-HYOSRQIV.js → replay-M3YKBVAM.js} +8 -8
  49. package/dist/cli/{run-2ZHADOUP.js → run-V6X5GXCR.js} +13 -13
  50. package/dist/cli/{server-X75PAZG5.js → server-5WVJQUOR.js} +10 -10
  51. package/dist/cli/{sessions-POOZA5CQ.js → sessions-B266WVM3.js} +12 -12
  52. package/dist/cli/{setup-YLPFI3OH.js → setup-SWX5E3W2.js} +5 -5
  53. package/dist/cli/{stats-NXJ3TO2D.js → stats-VPPKS6UF.js} +6 -6
  54. package/dist/cli/{version-NXXWE3WN.js → version-TVHAEHWY.js} +12 -12
  55. package/dist/index.d.ts +17 -2
  56. package/dist/index.js +360 -150
  57. package/dist/index.js.map +1 -1
  58. package/package.json +2 -2
  59. package/dist/cli/acp-35C4ME6Y.js.map +0 -1
  60. package/dist/cli/chunk-4MQ3VURH.js.map +0 -1
  61. package/dist/cli/chunk-4TVNJWMA.js.map +0 -1
  62. package/dist/cli/chunk-IAUOP25G.js.map +0 -1
  63. package/dist/cli/chunk-J5BYPUB5.js.map +0 -1
  64. package/dist/cli/chunk-QJG7OF27.js.map +0 -1
  65. package/dist/cli/chunk-TH756VLN.js.map +0 -1
  66. package/dist/cli/code-4TUTAGO5.js.map +0 -1
  67. /package/dist/cli/{chat-A6UJDPGV.js.map → chat-636MFZ7W.js.map} +0 -0
  68. /package/dist/cli/{chunk-JKGYMRX5.js.map → chunk-3N7FTZVE.js.map} +0 -0
  69. /package/dist/cli/{chunk-7L2WTRNU.js.map → chunk-ANVEA3RU.js.map} +0 -0
  70. /package/dist/cli/{chunk-QVC75MR3.js.map → chunk-BXMMGFAL.js.map} +0 -0
  71. /package/dist/cli/{chunk-UI66BH6D.js.map → chunk-COTWTQQZ.js.map} +0 -0
  72. /package/dist/cli/{chunk-XJ5SRLKK.js.map → chunk-D3ACJ6D5.js.map} +0 -0
  73. /package/dist/cli/{chunk-WRN65TRD.js.map → chunk-DSQNSP7F.js.map} +0 -0
  74. /package/dist/cli/{chunk-BSINVTTL.js.map → chunk-FXG7CSGY.js.map} +0 -0
  75. /package/dist/cli/{chunk-BSGCXZQN.js.map → chunk-LNU3CR7X.js.map} +0 -0
  76. /package/dist/cli/{chunk-3T6VBZCL.js.map → chunk-NQJYZKEU.js.map} +0 -0
  77. /package/dist/cli/{chunk-IX6XI2RG.js.map → chunk-OB5XR5HG.js.map} +0 -0
  78. /package/dist/cli/{chunk-ILJOIQ5W.js.map → chunk-OY5GGU6D.js.map} +0 -0
  79. /package/dist/cli/{chunk-CPKCNHRR.js.map → chunk-RSQMO6CF.js.map} +0 -0
  80. /package/dist/cli/{chunk-3OAR6NVL.js.map → chunk-RUPXIRNL.js.map} +0 -0
  81. /package/dist/cli/{chunk-4IBIPQVB.js.map → chunk-T6SBUSG2.js.map} +0 -0
  82. /package/dist/cli/{chunk-D5NFKRGO.js.map → chunk-UGPC4LPM.js.map} +0 -0
  83. /package/dist/cli/{chunk-T5TQ4NDT.js.map → chunk-X4UJ6Q6M.js.map} +0 -0
  84. /package/dist/cli/{commands-KMOZEYCF.js.map → commands-HMQPRVNT.js.map} +0 -0
  85. /package/dist/cli/{commit-DTFA56VQ.js.map → commit-WIY4B3X4.js.map} +0 -0
  86. /package/dist/cli/{desktop-7N3MHNBD.js.map → desktop-MGOG3AWV.js.map} +0 -0
  87. /package/dist/cli/{diff-E5OWTF4C.js.map → diff-57LRKCB7.js.map} +0 -0
  88. /package/dist/cli/{doctor-IEJQRJMN.js.map → doctor-5FDRBIXE.js.map} +0 -0
  89. /package/dist/cli/{mcp-PDI2PDLG.js.map → mcp-HJHTNRZF.js.map} +0 -0
  90. /package/dist/cli/{mcp-browse-OSPXOFPZ.js.map → mcp-browse-C2PJRQBO.js.map} +0 -0
  91. /package/dist/cli/{mcp-inspect-QRFVTHMF.js.map → mcp-inspect-JBFXV2II.js.map} +0 -0
  92. /package/dist/cli/{prompt-3CDII3UO.js.map → prompt-U62OVZNY.js.map} +0 -0
  93. /package/dist/cli/{replay-HYOSRQIV.js.map → replay-M3YKBVAM.js.map} +0 -0
  94. /package/dist/cli/{run-2ZHADOUP.js.map → run-V6X5GXCR.js.map} +0 -0
  95. /package/dist/cli/{server-X75PAZG5.js.map → server-5WVJQUOR.js.map} +0 -0
  96. /package/dist/cli/{sessions-POOZA5CQ.js.map → sessions-B266WVM3.js.map} +0 -0
  97. /package/dist/cli/{setup-YLPFI3OH.js.map → setup-SWX5E3W2.js.map} +0 -0
  98. /package/dist/cli/{stats-NXJ3TO2D.js.map → stats-VPPKS6UF.js.map} +0 -0
  99. /package/dist/cli/{version-NXXWE3WN.js.map → version-TVHAEHWY.js.map} +0 -0
@@ -3,7 +3,7 @@ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.requi
3
3
  import {
4
4
  MemoryStore,
5
5
  sanitizeMemoryName
6
- } from "./chunk-QJG7OF27.js";
6
+ } from "./chunk-FKSYTVWZ.js";
7
7
  import {
8
8
  countTokens,
9
9
  countTokensBounded,
@@ -12,12 +12,12 @@ import {
12
12
  } from "./chunk-6OWJV3YW.js";
13
13
  import {
14
14
  Usage
15
- } from "./chunk-BSGCXZQN.js";
15
+ } from "./chunk-LNU3CR7X.js";
16
16
  import {
17
17
  applyEdit,
18
18
  applyMultiEdit,
19
19
  pauseGate
20
- } from "./chunk-TH756VLN.js";
20
+ } from "./chunk-MXUSER5C.js";
21
21
  import {
22
22
  NEGATIVE_CLAIM_RULE,
23
23
  PROJECT_MEMORY_FILES,
@@ -28,7 +28,7 @@ import {
28
28
  import {
29
29
  formatHookOutcomeMessage,
30
30
  runHooks
31
- } from "./chunk-IX6XI2RG.js";
31
+ } from "./chunk-OB5XR5HG.js";
32
32
  import {
33
33
  ignoredByLayers,
34
34
  loadGitignoreAt,
@@ -46,10 +46,10 @@ import {
46
46
  DEEPSEEK_CONTEXT_TOKENS,
47
47
  DEFAULT_CONTEXT_TOKENS,
48
48
  SessionStats
49
- } from "./chunk-ILJOIQ5W.js";
49
+ } from "./chunk-OY5GGU6D.js";
50
50
  import {
51
51
  t
52
- } from "./chunk-IAUOP25G.js";
52
+ } from "./chunk-R677DIFU.js";
53
53
  import {
54
54
  DEFAULT_INDEX_EXCLUDES,
55
55
  addProjectPathAllowed,
@@ -59,7 +59,7 @@ import {
59
59
  require_picomatch,
60
60
  webSearchEndpoint,
61
61
  webSearchEngine
62
- } from "./chunk-4MQ3VURH.js";
62
+ } from "./chunk-K43DXH3G.js";
63
63
  import {
64
64
  __commonJS,
65
65
  __esm,
@@ -6415,9 +6415,14 @@ var ToolRegistry = class {
6415
6415
  rejectedReason: "plan-mode"
6416
6416
  });
6417
6417
  }
6418
+ const dispatchCtx = {
6419
+ signal: opts.signal,
6420
+ confirmationGate: opts.confirmationGate,
6421
+ onInteractiveWait: opts.onInteractiveWait
6422
+ };
6418
6423
  if (this._interceptor) {
6419
6424
  try {
6420
- const short = await this._interceptor(name, args);
6425
+ const short = await this._interceptor(name, args, dispatchCtx);
6421
6426
  if (typeof short === "string") return short;
6422
6427
  } catch (err) {
6423
6428
  return JSON.stringify({
@@ -6431,10 +6436,7 @@ var ToolRegistry = class {
6431
6436
  this._auditListener?.({ name, args });
6432
6437
  } catch {
6433
6438
  }
6434
- const result = await tool.fn(args, {
6435
- signal: opts.signal,
6436
- confirmationGate: opts.confirmationGate
6437
- });
6439
+ const result = await tool.fn(args, dispatchCtx);
6438
6440
  const str = typeof result === "string" ? result : JSON.stringify(result);
6439
6441
  let clipped = str;
6440
6442
  if (opts.maxResultTokens !== void 0) {
@@ -7814,6 +7816,8 @@ var CacheFirstLoop = class {
7814
7816
  const name = call.function?.name ?? "";
7815
7817
  const args = call.function?.arguments ?? "{}";
7816
7818
  const parsedArgs = safeParseToolArgs(args);
7819
+ const startedAt = Date.now();
7820
+ let interactiveWaitMs = 0;
7817
7821
  this._inflight.add(this.inflightIdFor(call));
7818
7822
  try {
7819
7823
  const preReport = await runHooks({
@@ -7833,13 +7837,17 @@ var CacheFirstLoop = class {
7833
7837
  preWarnings,
7834
7838
  postWarnings: [],
7835
7839
  result: `[hook block] ${blocking?.hook.command ?? "<unknown>"}
7836
- ${reason}`
7840
+ ${reason}`,
7841
+ elapsedMs: Date.now() - startedAt
7837
7842
  };
7838
7843
  }
7839
7844
  const result = await this.tools.dispatch(name, args, {
7840
7845
  signal,
7841
7846
  maxResultTokens: DEFAULT_MAX_RESULT_TOKENS,
7842
- confirmationGate: this.confirmationGate
7847
+ confirmationGate: this.confirmationGate,
7848
+ onInteractiveWait: (elapsedMs) => {
7849
+ interactiveWaitMs += Math.max(0, elapsedMs);
7850
+ }
7843
7851
  });
7844
7852
  const postReport = await runHooks({
7845
7853
  hooks: this.hooks,
@@ -7852,7 +7860,12 @@ ${reason}`
7852
7860
  }
7853
7861
  });
7854
7862
  const postWarnings = [...hookWarnings(postReport.outcomes, this._turn)];
7855
- return { preWarnings, postWarnings, result };
7863
+ return {
7864
+ preWarnings,
7865
+ postWarnings,
7866
+ result,
7867
+ elapsedMs: Math.max(0, Date.now() - startedAt - interactiveWaitMs)
7868
+ };
7856
7869
  } finally {
7857
7870
  this._inflight.delete(this.inflightIdFor(call));
7858
7871
  }
@@ -8337,12 +8350,14 @@ ${reason}`
8337
8350
  const args = call.function?.arguments ?? "{}";
8338
8351
  const s = settled[k];
8339
8352
  let result;
8353
+ let elapsedMs = 0;
8340
8354
  let preWarnings = [];
8341
8355
  let postWarnings = [];
8342
8356
  if (s.status === "fulfilled") {
8343
8357
  preWarnings = s.value.preWarnings;
8344
8358
  postWarnings = s.value.postWarnings;
8345
8359
  result = s.value.result;
8360
+ elapsedMs = s.value.elapsedMs;
8346
8361
  } else {
8347
8362
  const err = s.reason instanceof Error ? s.reason : new Error(String(s.reason));
8348
8363
  result = JSON.stringify({ error: `${err.name}: ${err.message}` });
@@ -8361,7 +8376,8 @@ ${reason}`
8361
8376
  content: result,
8362
8377
  toolName: name,
8363
8378
  toolArgs: args,
8364
- callId: this.inflightIdFor(call)
8379
+ callId: this.inflightIdFor(call),
8380
+ elapsedMs
8365
8381
  };
8366
8382
  }
8367
8383
  }
@@ -9929,6 +9945,110 @@ function formatOutline(entries) {
9929
9945
  ].join("\n");
9930
9946
  }
9931
9947
 
9948
+ // src/tools/fs/patch.ts
9949
+ function parseUnifiedPatch(patch) {
9950
+ if (typeof patch !== "string" || patch.trim().length === 0) {
9951
+ throw new Error("apply_patch: patch must be a non-empty string");
9952
+ }
9953
+ const lines = patch.split(/\n/);
9954
+ const files = [];
9955
+ let current = null;
9956
+ const ensureCurrent = () => {
9957
+ if (!current) {
9958
+ current = { oldPath: null, newPath: null, blocks: [] };
9959
+ files.push(current);
9960
+ }
9961
+ return current;
9962
+ };
9963
+ let i = 0;
9964
+ while (i < lines.length) {
9965
+ const line = lines[i];
9966
+ if (line.startsWith("diff --git ")) {
9967
+ current = { oldPath: null, newPath: null, blocks: [] };
9968
+ files.push(current);
9969
+ i++;
9970
+ continue;
9971
+ }
9972
+ if (line.startsWith("--- ")) {
9973
+ ensureCurrent().oldPath = parsePatchPath(line.slice(4));
9974
+ i++;
9975
+ continue;
9976
+ }
9977
+ if (line.startsWith("+++ ")) {
9978
+ ensureCurrent().newPath = parsePatchPath(line.slice(4));
9979
+ i++;
9980
+ continue;
9981
+ }
9982
+ if (line.startsWith("@@")) {
9983
+ const file = ensureCurrent();
9984
+ const path = file.newPath ?? file.oldPath;
9985
+ if (!path) throw new Error("apply_patch: hunk is missing a file path");
9986
+ const searchLines = [];
9987
+ const replaceLines = [];
9988
+ i++;
9989
+ while (i < lines.length) {
9990
+ const hunkLine = lines[i];
9991
+ if (hunkLine.startsWith("diff --git ") || hunkLine.startsWith("@@") || hunkLine.startsWith("--- ")) {
9992
+ break;
9993
+ }
9994
+ if (hunkLine === "" && i === lines.length - 1) {
9995
+ break;
9996
+ }
9997
+ if (hunkLine.startsWith("\")) {
9998
+ i++;
9999
+ continue;
10000
+ }
10001
+ const marker = hunkLine[0];
10002
+ const body = hunkLine.slice(1);
10003
+ if (marker === " ") {
10004
+ searchLines.push(body);
10005
+ replaceLines.push(body);
10006
+ } else if (marker === "-") {
10007
+ searchLines.push(body);
10008
+ } else if (marker === "+") {
10009
+ replaceLines.push(body);
10010
+ } else if (hunkLine.trim().length === 0) {
10011
+ } else {
10012
+ throw new Error(`apply_patch: invalid hunk line: ${hunkLine}`);
10013
+ }
10014
+ i++;
10015
+ }
10016
+ file.blocks.push({
10017
+ path,
10018
+ search: file.oldPath === null ? "" : linesToText(searchLines),
10019
+ replace: linesToText(replaceLines),
10020
+ offset: 0
10021
+ });
10022
+ continue;
10023
+ }
10024
+ i++;
10025
+ }
10026
+ const blocks = files.flatMap((file) => file.blocks);
10027
+ if (blocks.length === 0) throw new Error("apply_patch: patch did not contain any hunks");
10028
+ return blocks;
10029
+ }
10030
+ function linesToText(lines) {
10031
+ if (lines.length === 0) return "";
10032
+ return `${lines.join("\n")}
10033
+ `;
10034
+ }
10035
+ function parsePatchPath(raw) {
10036
+ const cleaned = unquotePath(raw.trim().split(/\t/, 1)[0] ?? "");
10037
+ if (cleaned === "/dev/null") return null;
10038
+ const withoutPrefix = cleaned.replace(/^[ab]\//, "");
10039
+ const normalized = withoutPrefix.replace(/^[/\\]+/, "");
10040
+ if (!normalized) throw new Error(`apply_patch: invalid patch path: ${raw}`);
10041
+ return normalized.replaceAll("\\", "/");
10042
+ }
10043
+ function unquotePath(path) {
10044
+ if (path.length < 2 || !path.startsWith('"') || !path.endsWith('"')) return path;
10045
+ try {
10046
+ return JSON.parse(path);
10047
+ } catch {
10048
+ return path.slice(1, -1);
10049
+ }
10050
+ }
10051
+
9932
10052
  // src/tools/fs/search.ts
9933
10053
  import { promises as fs2 } from "fs";
9934
10054
  import * as pathMod3 from "path";
@@ -10226,10 +10346,14 @@ ${body}`;
10226
10346
  let pending = inflightGate.get(allowPrefix);
10227
10347
  if (!pending) {
10228
10348
  const gate = ctx?.confirmationGate ?? pauseGate;
10349
+ const waitStartedAt = Date.now();
10229
10350
  pending = gate.ask({
10230
10351
  kind: "path_access",
10231
10352
  payload: { path: abs, intent, toolName, sandboxRoot: normRoot, allowPrefix }
10232
10353
  });
10354
+ pending = pending.finally(() => {
10355
+ ctx?.onInteractiveWait?.(Date.now() - waitStartedAt);
10356
+ });
10233
10357
  inflightGate.set(allowPrefix, pending);
10234
10358
  void pending.finally(() => inflightGate.delete(allowPrefix));
10235
10359
  }
@@ -10649,7 +10773,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
10649
10773
  });
10650
10774
  registry.register({
10651
10775
  name: "multi_edit",
10652
- description: "Apply N SEARCH/REPLACE edits across ONE OR MORE files in a single atomic call. Edits run sequentially in array order; for edits that touch the same file, a later edit can match text inserted by an earlier one. If ANY edit fails (search not found, ambiguous match, empty search, file unreadable), NO files are written \u2014 atomic at the validation layer. Same per-edit rules as edit_file: `search` is exact text (whitespace sensitive, no regex) and must be unique in its target file at the moment that edit applies. Use this for renames spanning multiple files, cross-file refactors, or any batch where you'd otherwise loop edit_file.",
10776
+ description: "Apply N SEARCH/REPLACE edits across ONE OR MORE files in a single atomic call. Edits run sequentially in array order; for edits that touch the same file, a later edit can match text inserted by an earlier one. If ANY edit fails (search not found, ambiguous match, file unreadable, invalid create), NO files are written \u2014 atomic at the validation layer. Same per-edit rules as edit_file: `search` is exact text (whitespace sensitive, no regex) and must be unique in its target file at the moment that edit applies. To create a new file, use an empty `search`; empty `search` is refused for existing files. Use this for renames spanning multiple files, cross-file refactors, or any batch where you'd otherwise loop edit_file.",
10653
10777
  parameters: {
10654
10778
  type: "object",
10655
10779
  properties: {
@@ -10686,6 +10810,41 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
10686
10810
  return applyMultiEdit(rootDir, resolved);
10687
10811
  }
10688
10812
  });
10813
+ registry.register({
10814
+ name: "apply_patch",
10815
+ description: "Apply a unified git-style patch atomically under the sandbox root. Use this for non-trivial code edits because it lets the UI review the whole patch as one batch. If any hunk fails, no files are written.",
10816
+ parameters: {
10817
+ type: "object",
10818
+ properties: {
10819
+ patch: {
10820
+ type: "string",
10821
+ description: "Unified diff text with diff --git / --- / +++ headers and @@ hunks. Paths should be project-relative or a/ b/-prefixed."
10822
+ }
10823
+ },
10824
+ required: ["patch"]
10825
+ },
10826
+ fn: async (args, ctx) => {
10827
+ try {
10828
+ const blocks = parseUnifiedPatch(args.patch);
10829
+ const resolved = await Promise.all(
10830
+ blocks.map(async (block) => ({
10831
+ abs: await safePath(block.path, "apply_patch", ctx, "write"),
10832
+ search: block.search,
10833
+ replace: block.replace,
10834
+ rel: block.path,
10835
+ created: block.search.length === 0
10836
+ }))
10837
+ );
10838
+ const output = await applyMultiEdit(rootDir, resolved);
10839
+ const fileCount = new Set(resolved.map((edit) => edit.rel)).size;
10840
+ const fileNoun = fileCount === 1 ? "file" : "files";
10841
+ const created = resolved.filter((edit) => edit.created).map((edit) => `created ${edit.rel}`);
10842
+ return [`apply_patch: applied ${fileCount} ${fileNoun}`, ...created, output].join("\n");
10843
+ } catch (err) {
10844
+ return `apply_patch: no files written \u2014 ${err.message}`;
10845
+ }
10846
+ }
10847
+ });
10689
10848
  registry.register({
10690
10849
  name: "create_directory",
10691
10850
  description: "Create a directory (and any missing parents) under the sandbox root.",
@@ -11594,6 +11753,7 @@ export {
11594
11753
  detectAtPicker,
11595
11754
  rankPickerCandidates,
11596
11755
  expandAtMentions,
11756
+ parseUnifiedPatch,
11597
11757
  looksLikeAbsoluteSystemPath,
11598
11758
  pathIsUnder,
11599
11759
  registerFilesystemTools,
@@ -11616,4 +11776,4 @@ export {
11616
11776
  he/he.js:
11617
11777
  (*! https://mths.be/he v1.2.0 by @mathias | MIT license *)
11618
11778
  */
11619
- //# sourceMappingURL=chunk-4TVNJWMA.js.map
11779
+ //# sourceMappingURL=chunk-ACHQFKW2.js.map