codexslimedit 0.7.4-dev.29700855995.1 → 0.7.4-dev.29702567006.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.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Readable, Writable } from "node:stream";
2
2
 
3
3
  /** Current independent codexslimedit package version. */
4
- export declare const CODEX_SLIM_EDIT_VERSION = "0.1.0";
4
+ export declare const CODEX_SLIM_EDIT_VERSION = "0.7.4-dev.29702567006.1";
5
5
 
6
6
  /** Returns whether command-line arguments request the package version. */
7
7
  export declare function isVersionRequest(args: readonly string[]): boolean;
package/dist/mcp.js CHANGED
@@ -4523,6 +4523,7 @@ async function readWorkspaceFile(input) {
4523
4523
  async function editWorkspaceFile(input) {
4524
4524
  const target = await resolveWorkspaceFile(input);
4525
4525
  const nextContent = replaceContent(await readUtf8Text(target.absolutePath), input.oldString, input.newString);
4526
+ validateText(nextContent);
4526
4527
  await atomicWrite(target.absolutePath, nextContent);
4527
4528
  return {
4528
4529
  path: target.relativePath,
@@ -4562,9 +4563,19 @@ async function createWorkspaceFile(input) {
4562
4563
  content: input.content
4563
4564
  };
4564
4565
  }
4566
+ /** Validates a new workspace file without modifying the workspace. */
4567
+ async function prepareWorkspaceFileCreation(input) {
4568
+ const target = await resolveNewWorkspaceFile(input, false);
4569
+ validateText(input.content);
4570
+ return {
4571
+ path: target.relativePath,
4572
+ content: input.content
4573
+ };
4574
+ }
4565
4575
  /** Deletes one existing regular workspace file. */
4566
4576
  async function deleteWorkspaceFile(input) {
4567
4577
  const target = await resolveWorkspaceFile(input);
4578
+ if ((await lstat(resolve(await existingDirectory(input.root), normalizeSeparators(input.filePath)))).isSymbolicLink()) throw new WorkspaceFileError("NOT_A_FILE", "Deleting symbolic links is not supported.");
4568
4579
  const content = await readUtf8Text(target.absolutePath);
4569
4580
  try {
4570
4581
  await rm(target.absolutePath);
@@ -4587,7 +4598,7 @@ async function resolveWorkspaceFile(input) {
4587
4598
  relativePath: relative(rootPath, resolvedCandidate).split(sep).join("/")
4588
4599
  };
4589
4600
  }
4590
- async function resolveNewWorkspaceFile(input) {
4601
+ async function resolveNewWorkspaceFile(input, createParent = true) {
4591
4602
  const rootPath = await existingDirectory(input.root);
4592
4603
  const candidate = resolve(rootPath, normalizeSeparators(input.filePath));
4593
4604
  if (!isInside(rootPath, candidate)) throw new WorkspaceFileError("PATH_OUTSIDE_ROOT", "filePath must remain inside the workspace root.");
@@ -4597,13 +4608,13 @@ async function resolveNewWorkspaceFile(input) {
4597
4608
  } catch (error) {
4598
4609
  if (error instanceof WorkspaceFileError) throw error;
4599
4610
  }
4600
- const absolutePath = resolve(await createSafeParent(rootPath, dirname(candidate)), basename(candidate));
4611
+ const absolutePath = resolve(await safeParent(rootPath, dirname(candidate), createParent), basename(candidate));
4601
4612
  return {
4602
4613
  absolutePath,
4603
4614
  relativePath: relative(rootPath, absolutePath).split(sep).join("/")
4604
4615
  };
4605
4616
  }
4606
- async function createSafeParent(rootPath, requestedParent) {
4617
+ async function safeParent(rootPath, requestedParent, createParent) {
4607
4618
  const missingDirectories = [];
4608
4619
  let existingParent = requestedParent;
4609
4620
  while (true) try {
@@ -4616,7 +4627,7 @@ async function createSafeParent(rootPath, requestedParent) {
4616
4627
  }
4617
4628
  if (existingParent !== rootPath && !isInside(rootPath, existingParent)) throw new WorkspaceFileError("PATH_OUTSIDE_ROOT", "filePath parent resolves outside the workspace root through a symlink.");
4618
4629
  const parentPath = resolve(existingParent, ...missingDirectories.reverse());
4619
- await mkdir(parentPath, { recursive: true });
4630
+ if (createParent) await mkdir(parentPath, { recursive: true });
4620
4631
  return parentPath;
4621
4632
  }
4622
4633
  async function existingDirectory(root) {
@@ -4654,7 +4665,10 @@ async function readUtf8Text(path) {
4654
4665
  throw new WorkspaceFileError("UNREADABLE_FILE", "filePath could not be read.");
4655
4666
  }
4656
4667
  try {
4657
- const content = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
4668
+ const content = new TextDecoder("utf-8", {
4669
+ fatal: true,
4670
+ ignoreBOM: true
4671
+ }).decode(bytes);
4658
4672
  validateText(content);
4659
4673
  return content;
4660
4674
  } catch {
@@ -4765,38 +4779,69 @@ function message(error) {
4765
4779
  /** Applies Codex add, update, and delete patch operations inside a workspace. */
4766
4780
  async function applyWorkspacePatch(input) {
4767
4781
  const operations = parsePatch(input.patch);
4782
+ const planned = await planOperations(input.root, operations);
4768
4783
  const paths = [];
4769
- for (const operation of operations) {
4784
+ for (const { operation, path, content } of planned) {
4770
4785
  if (operation.kind === "add") {
4771
- const result = await createWorkspaceFile({
4786
+ await createWorkspaceFile({
4772
4787
  root: input.root,
4773
4788
  filePath: operation.filePath,
4774
4789
  content: operation.content
4775
4790
  });
4776
- paths.push(result.path);
4791
+ paths.push(path);
4777
4792
  continue;
4778
4793
  }
4779
4794
  if (operation.kind === "delete") {
4780
- const result = await deleteWorkspaceFile({
4795
+ await deleteWorkspaceFile({
4781
4796
  root: input.root,
4782
4797
  filePath: operation.filePath
4783
4798
  });
4784
- paths.push(result.path);
4799
+ paths.push(path);
4785
4800
  continue;
4786
4801
  }
4787
- const current = await readWorkspaceFile({
4788
- root: input.root,
4789
- filePath: operation.filePath
4790
- });
4791
- const result = await writeWorkspaceFile({
4802
+ await writeWorkspaceFile({
4792
4803
  root: input.root,
4793
4804
  filePath: operation.filePath,
4794
- content: applyUpdateHunks(current.content, operation.hunks)
4805
+ content: content ?? ""
4795
4806
  });
4796
- paths.push(result.path);
4807
+ paths.push(path);
4797
4808
  }
4798
4809
  return { paths };
4799
4810
  }
4811
+ async function planOperations(root, operations) {
4812
+ const planned = [];
4813
+ const paths = /* @__PURE__ */ new Set();
4814
+ for (const operation of operations) {
4815
+ if (operation.kind === "add") {
4816
+ const result = await prepareWorkspaceFileCreation({
4817
+ root,
4818
+ filePath: operation.filePath,
4819
+ content: operation.content
4820
+ });
4821
+ assertUniquePath(paths, result.path);
4822
+ planned.push({
4823
+ operation,
4824
+ path: result.path
4825
+ });
4826
+ continue;
4827
+ }
4828
+ const current = await readWorkspaceFile({
4829
+ root,
4830
+ filePath: operation.filePath
4831
+ });
4832
+ assertUniquePath(paths, current.path);
4833
+ planned.push({
4834
+ operation,
4835
+ path: current.path,
4836
+ content: operation.kind === "update" ? applyUpdateHunks(current.content, operation.hunks) : void 0
4837
+ });
4838
+ }
4839
+ return planned;
4840
+ }
4841
+ function assertUniquePath(paths, path) {
4842
+ if (paths.has(path)) invalidPatch(`Patch contains multiple operations for ${path}.`);
4843
+ paths.add(path);
4844
+ }
4800
4845
  function parsePatch(patch) {
4801
4846
  const lines = patch.replace(/\r\n|\r/g, "\n").split("\n");
4802
4847
  while (lines.at(-1) === "") lines.pop();
@@ -4811,8 +4856,12 @@ function parsePatch(patch) {
4811
4856
  index += 1;
4812
4857
  if (addPath !== void 0) {
4813
4858
  const contentLines = [];
4814
- while (index < lines.length - 1 && !lines[index]?.startsWith("*** ")) {
4859
+ while (index < lines.length - 1 && !isOperationHeader(lines[index])) {
4815
4860
  const line = lines[index];
4861
+ if (line === "*** End of File") {
4862
+ index += 1;
4863
+ break;
4864
+ }
4816
4865
  if (!line?.startsWith("+")) invalidPatch("Add File lines must start with `+`.");
4817
4866
  contentLines.push(line.slice(1));
4818
4867
  index += 1;
@@ -4835,8 +4884,12 @@ function parsePatch(patch) {
4835
4884
  const hunks = [];
4836
4885
  let oldLines;
4837
4886
  let newLines;
4838
- while (index < lines.length - 1 && !lines[index]?.startsWith("*** ")) {
4887
+ while (index < lines.length - 1 && !isOperationHeader(lines[index])) {
4839
4888
  const line = lines[index];
4889
+ if (line === "*** End of File") {
4890
+ index += 1;
4891
+ break;
4892
+ }
4840
4893
  if (line?.startsWith("@@")) {
4841
4894
  if (oldLines !== void 0 && newLines !== void 0) hunks.push(validHunk(oldLines, newLines));
4842
4895
  oldLines = [];
@@ -4866,6 +4919,9 @@ function parsePatch(patch) {
4866
4919
  if (operations.length === 0) invalidPatch("Patch must contain at least one file operation.");
4867
4920
  return operations;
4868
4921
  }
4922
+ function isOperationHeader(line) {
4923
+ return /^\*\*\* (?:Add|Update|Delete) File: /.test(line ?? "");
4924
+ }
4869
4925
  function validHunk(oldLines, newLines) {
4870
4926
  if (oldLines.length === 0) invalidPatch("Update hunks require old or context lines for deterministic placement.");
4871
4927
  return {
@@ -4906,7 +4962,7 @@ function invalidPatch(message) {
4906
4962
  //#endregion
4907
4963
  //#region src/version.ts
4908
4964
  /** Current independent codexslimedit package version. */
4909
- var CODEX_SLIM_EDIT_VERSION = "0.7.4-dev.29700855995.1";
4965
+ var CODEX_SLIM_EDIT_VERSION = "0.7.4-dev.29702567006.1";
4910
4966
  /** Returns whether command-line arguments request the package version. */
4911
4967
  function isVersionRequest(args) {
4912
4968
  return args.includes("--version");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexslimedit",
3
- "version": "0.7.4-dev.29700855995.1",
3
+ "version": "0.7.4-dev.29702567006.1",
4
4
  "description": "Concise, workspace-safe MCP file reading and editing.",
5
5
  "license": "SUL-1.0",
6
6
  "repository": {