codexslimedit 0.7.4-dev.29698722339.1 → 0.7.4-dev.29700855995.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/README.md +4 -4
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +42 -0
- package/dist/index.js +2 -2
- package/dist/mcp.js +295 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@ bunx codexslimedit
|
|
|
12
12
|
|
|
13
13
|
The MCP server exposes:
|
|
14
14
|
|
|
15
|
-
- `
|
|
16
|
-
- `
|
|
15
|
+
- `read_file`: returns a root-relative path and file content without footer boilerplate.
|
|
16
|
+
- `apply_patch`: accepts Codex `*** Begin Patch` envelopes for `Add File`, `Update File`, and `Delete File`. It also retains a compact `filePath`/`oldString`/`newString` form for one unique exact replacement or inclusive line range such as `55-64`.
|
|
17
17
|
|
|
18
|
-
Both tools constrain access to the server working directory. Reads reject non-UTF-8, NUL-containing, missing, non-file, traversal, and symlink-escape targets.
|
|
18
|
+
Both tools constrain access to the server working directory. Reads reject non-UTF-8, NUL-containing, missing, non-file, traversal, and symlink-escape targets. Patches create, update, and delete regular UTF-8 files; exact edits reject ambiguous matches and invalid ranges, preserve line endings and file mode, and use same-directory atomic replacement.
|
|
19
19
|
|
|
20
20
|
## Codex adaptation
|
|
21
21
|
|
|
22
|
-
This package adapts ideas from OpenSlimEdit for supported Codex extension surfaces. Codex
|
|
22
|
+
This package adapts ideas from OpenSlimEdit for supported Codex extension surfaces. Codex qualifies MCP tool names with their server namespace, so the model-facing names are `mcp__codexslimedit__read_file` and `mcp__codexslimedit__apply_patch`; MCP cannot overwrite Codex's built-in `apply_patch` registration. HolyCodex instructions require these tools for workspace reads and writes. Native patch envelopes preserve familiar patch ergonomics, while the compact exact-replacement form reduces simple-edit arguments and results. No upstream token-saving percentage is claimed without Codex-specific benchmarks.
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { d as stackOrMessageFromError, i as isVersionRequest, n as runCodexSlimEditMcpStdioServer, r as CODEX_SLIM_EDIT_VERSION } from "./mcp.js";
|
|
3
3
|
import { stderr } from "node:process";
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
/** Starts the CodexSlimEdit MCP stdio entrypoint. */
|
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,10 @@ export declare function isVersionRequest(args: readonly string[]): boolean;
|
|
|
8
8
|
|
|
9
9
|
/** Identifies a workspace file operation failure. */
|
|
10
10
|
export type WorkspaceFileErrorCode =
|
|
11
|
+
| "ALREADY_EXISTS"
|
|
11
12
|
| "EXACT_MATCH_NOT_FOUND"
|
|
12
13
|
| "DUPLICATE_MATCH"
|
|
14
|
+
| "INVALID_PATCH"
|
|
13
15
|
| "INVALID_RANGE"
|
|
14
16
|
| "NOT_A_FILE"
|
|
15
17
|
| "NOT_FOUND"
|
|
@@ -49,6 +51,26 @@ export interface WorkspaceFileResult {
|
|
|
49
51
|
readonly content: string;
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
/** Input for writing complete UTF-8 content to a workspace file. */
|
|
55
|
+
export interface WriteWorkspaceFileInput extends WorkspaceFileInput {
|
|
56
|
+
/** Complete replacement content. */
|
|
57
|
+
readonly content: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Input for a Codex-compatible workspace patch envelope. */
|
|
61
|
+
export interface ApplyWorkspacePatchInput {
|
|
62
|
+
/** Workspace root used to constrain file access. */
|
|
63
|
+
readonly root: string;
|
|
64
|
+
/** Codex `*** Begin Patch` envelope. */
|
|
65
|
+
readonly patch: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Paths changed by a workspace patch. */
|
|
69
|
+
export interface WorkspacePatchResult {
|
|
70
|
+
/** Canonical root-relative paths in patch order. */
|
|
71
|
+
readonly paths: readonly string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
/** Options for the CodexSlimEdit MCP server. */
|
|
53
75
|
export interface CodexSlimEditMcpOptions {
|
|
54
76
|
/** Workspace root; defaults to the server process current directory. */
|
|
@@ -63,6 +85,26 @@ export declare function editWorkspaceFile(
|
|
|
63
85
|
input: EditWorkspaceFileInput,
|
|
64
86
|
): Promise<WorkspaceFileResult>;
|
|
65
87
|
|
|
88
|
+
/** Replaces an existing workspace file with complete UTF-8 content. */
|
|
89
|
+
export declare function writeWorkspaceFile(
|
|
90
|
+
input: WriteWorkspaceFileInput,
|
|
91
|
+
): Promise<WorkspaceFileResult>;
|
|
92
|
+
|
|
93
|
+
/** Creates a new workspace file in an existing directory. */
|
|
94
|
+
export declare function createWorkspaceFile(
|
|
95
|
+
input: WriteWorkspaceFileInput,
|
|
96
|
+
): Promise<WorkspaceFileResult>;
|
|
97
|
+
|
|
98
|
+
/** Deletes one existing regular workspace file. */
|
|
99
|
+
export declare function deleteWorkspaceFile(
|
|
100
|
+
input: WorkspaceFileInput,
|
|
101
|
+
): Promise<WorkspaceFileResult>;
|
|
102
|
+
|
|
103
|
+
/** Applies Codex add, update, and delete patch operations inside a workspace. */
|
|
104
|
+
export declare function applyWorkspacePatch(
|
|
105
|
+
input: ApplyWorkspacePatchInput,
|
|
106
|
+
): Promise<WorkspacePatchResult>;
|
|
107
|
+
|
|
66
108
|
/** Handles one CodexSlimEdit MCP JSON-RPC request. */
|
|
67
109
|
export declare function handleCodexSlimEditMcpRequest(
|
|
68
110
|
input: unknown,
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { CODEX_SLIM_EDIT_VERSION, WorkspaceFileError, editWorkspaceFile, handleCodexSlimEditMcpRequest, isVersionRequest, readWorkspaceFile, runCodexSlimEditMcpStdioServer };
|
|
1
|
+
import { a as applyWorkspacePatch, c as editWorkspaceFile, f as WorkspaceFileError, i as isVersionRequest, l as readWorkspaceFile, n as runCodexSlimEditMcpStdioServer, o as createWorkspaceFile, r as CODEX_SLIM_EDIT_VERSION, s as deleteWorkspaceFile, t as handleCodexSlimEditMcpRequest, u as writeWorkspaceFile } from "./mcp.js";
|
|
2
|
+
export { CODEX_SLIM_EDIT_VERSION, WorkspaceFileError, applyWorkspacePatch, createWorkspaceFile, deleteWorkspaceFile, editWorkspaceFile, handleCodexSlimEditMcpRequest, isVersionRequest, readWorkspaceFile, runCodexSlimEditMcpStdioServer, writeWorkspaceFile };
|
package/dist/mcp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "node:child_process";
|
|
2
|
-
import { chmod, open, readFile, realpath, rename, rm, stat } from "node:fs/promises";
|
|
2
|
+
import { chmod, lstat, mkdir, open, readFile, realpath, rename, rm, stat } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
4
|
//#region src/errors.ts
|
|
5
5
|
/** Reports a typed, actionable workspace file operation failure. */
|
|
@@ -4508,14 +4508,6 @@ function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
|
|
|
4508
4508
|
};
|
|
4509
4509
|
}
|
|
4510
4510
|
//#endregion
|
|
4511
|
-
//#region src/version.ts
|
|
4512
|
-
/** Current independent codexslimedit package version. */
|
|
4513
|
-
var CODEX_SLIM_EDIT_VERSION = "0.7.4-dev.29698722339.1";
|
|
4514
|
-
/** Returns whether command-line arguments request the package version. */
|
|
4515
|
-
function isVersionRequest(args) {
|
|
4516
|
-
return args.includes("--version");
|
|
4517
|
-
}
|
|
4518
|
-
//#endregion
|
|
4519
4511
|
//#region src/workspace.ts
|
|
4520
4512
|
var RANGE_PATTERN = /^\s*(?<start>[1-9]\d*)\s*(?:-\s*(?<end>[1-9]\d*)\s*)?$/;
|
|
4521
4513
|
var TEMPORARY_FILE_PREFIX = ".codexslimedit-";
|
|
@@ -4537,6 +4529,53 @@ async function editWorkspaceFile(input) {
|
|
|
4537
4529
|
content: nextContent
|
|
4538
4530
|
};
|
|
4539
4531
|
}
|
|
4532
|
+
/** Replaces an existing workspace file with complete UTF-8 content. */
|
|
4533
|
+
async function writeWorkspaceFile(input) {
|
|
4534
|
+
const target = await resolveWorkspaceFile(input);
|
|
4535
|
+
validateText(input.content);
|
|
4536
|
+
await atomicWrite(target.absolutePath, input.content);
|
|
4537
|
+
return {
|
|
4538
|
+
path: target.relativePath,
|
|
4539
|
+
content: input.content
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
/** Creates a new workspace file in an existing directory. */
|
|
4543
|
+
async function createWorkspaceFile(input) {
|
|
4544
|
+
const target = await resolveNewWorkspaceFile(input);
|
|
4545
|
+
validateText(input.content);
|
|
4546
|
+
let file;
|
|
4547
|
+
let created = false;
|
|
4548
|
+
try {
|
|
4549
|
+
file = await open(target.absolutePath, "wx", 420);
|
|
4550
|
+
created = true;
|
|
4551
|
+
await file.writeFile(input.content, "utf8");
|
|
4552
|
+
await file.sync();
|
|
4553
|
+
await file.close();
|
|
4554
|
+
file = void 0;
|
|
4555
|
+
} catch (error) {
|
|
4556
|
+
await file?.close();
|
|
4557
|
+
if (created) await rm(target.absolutePath, { force: true });
|
|
4558
|
+
throw new WorkspaceFileError("WRITE_FAILED", `Could not create filePath: ${message(error)}`);
|
|
4559
|
+
}
|
|
4560
|
+
return {
|
|
4561
|
+
path: target.relativePath,
|
|
4562
|
+
content: input.content
|
|
4563
|
+
};
|
|
4564
|
+
}
|
|
4565
|
+
/** Deletes one existing regular workspace file. */
|
|
4566
|
+
async function deleteWorkspaceFile(input) {
|
|
4567
|
+
const target = await resolveWorkspaceFile(input);
|
|
4568
|
+
const content = await readUtf8Text(target.absolutePath);
|
|
4569
|
+
try {
|
|
4570
|
+
await rm(target.absolutePath);
|
|
4571
|
+
} catch (error) {
|
|
4572
|
+
throw new WorkspaceFileError("WRITE_FAILED", `Could not delete filePath: ${message(error)}`);
|
|
4573
|
+
}
|
|
4574
|
+
return {
|
|
4575
|
+
path: target.relativePath,
|
|
4576
|
+
content
|
|
4577
|
+
};
|
|
4578
|
+
}
|
|
4540
4579
|
async function resolveWorkspaceFile(input) {
|
|
4541
4580
|
const rootPath = await existingDirectory(input.root);
|
|
4542
4581
|
const candidate = resolve(rootPath, normalizeSeparators(input.filePath));
|
|
@@ -4548,6 +4587,38 @@ async function resolveWorkspaceFile(input) {
|
|
|
4548
4587
|
relativePath: relative(rootPath, resolvedCandidate).split(sep).join("/")
|
|
4549
4588
|
};
|
|
4550
4589
|
}
|
|
4590
|
+
async function resolveNewWorkspaceFile(input) {
|
|
4591
|
+
const rootPath = await existingDirectory(input.root);
|
|
4592
|
+
const candidate = resolve(rootPath, normalizeSeparators(input.filePath));
|
|
4593
|
+
if (!isInside(rootPath, candidate)) throw new WorkspaceFileError("PATH_OUTSIDE_ROOT", "filePath must remain inside the workspace root.");
|
|
4594
|
+
try {
|
|
4595
|
+
await lstat(candidate);
|
|
4596
|
+
throw new WorkspaceFileError("ALREADY_EXISTS", "filePath already exists.");
|
|
4597
|
+
} catch (error) {
|
|
4598
|
+
if (error instanceof WorkspaceFileError) throw error;
|
|
4599
|
+
}
|
|
4600
|
+
const absolutePath = resolve(await createSafeParent(rootPath, dirname(candidate)), basename(candidate));
|
|
4601
|
+
return {
|
|
4602
|
+
absolutePath,
|
|
4603
|
+
relativePath: relative(rootPath, absolutePath).split(sep).join("/")
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
async function createSafeParent(rootPath, requestedParent) {
|
|
4607
|
+
const missingDirectories = [];
|
|
4608
|
+
let existingParent = requestedParent;
|
|
4609
|
+
while (true) try {
|
|
4610
|
+
existingParent = await realpath(existingParent);
|
|
4611
|
+
break;
|
|
4612
|
+
} catch {
|
|
4613
|
+
if (existingParent === rootPath) throw new WorkspaceFileError("NOT_FOUND", "Workspace root does not exist.");
|
|
4614
|
+
missingDirectories.push(basename(existingParent));
|
|
4615
|
+
existingParent = dirname(existingParent);
|
|
4616
|
+
}
|
|
4617
|
+
if (existingParent !== rootPath && !isInside(rootPath, existingParent)) throw new WorkspaceFileError("PATH_OUTSIDE_ROOT", "filePath parent resolves outside the workspace root through a symlink.");
|
|
4618
|
+
const parentPath = resolve(existingParent, ...missingDirectories.reverse());
|
|
4619
|
+
await mkdir(parentPath, { recursive: true });
|
|
4620
|
+
return parentPath;
|
|
4621
|
+
}
|
|
4551
4622
|
async function existingDirectory(root) {
|
|
4552
4623
|
try {
|
|
4553
4624
|
const resolvedRoot = await realpath(resolve(root));
|
|
@@ -4584,12 +4655,15 @@ async function readUtf8Text(path) {
|
|
|
4584
4655
|
}
|
|
4585
4656
|
try {
|
|
4586
4657
|
const content = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
4587
|
-
|
|
4658
|
+
validateText(content);
|
|
4588
4659
|
return content;
|
|
4589
4660
|
} catch {
|
|
4590
4661
|
throw new WorkspaceFileError("UNSUPPORTED_TEXT", "filePath must contain UTF-8 text without NUL bytes.");
|
|
4591
4662
|
}
|
|
4592
4663
|
}
|
|
4664
|
+
function validateText(content) {
|
|
4665
|
+
if (content.includes("\0")) throw new WorkspaceFileError("UNSUPPORTED_TEXT", "Text must not contain NUL bytes.");
|
|
4666
|
+
}
|
|
4593
4667
|
function replaceContent(content, oldString, newString) {
|
|
4594
4668
|
const range = parseRange(oldString, lineCount(content));
|
|
4595
4669
|
if (range !== null) return replaceLineRange(content, range.start, range.end, newString);
|
|
@@ -4687,14 +4761,168 @@ function message(error) {
|
|
|
4687
4761
|
return error instanceof Error ? error.message : String(error);
|
|
4688
4762
|
}
|
|
4689
4763
|
//#endregion
|
|
4764
|
+
//#region src/patch.ts
|
|
4765
|
+
/** Applies Codex add, update, and delete patch operations inside a workspace. */
|
|
4766
|
+
async function applyWorkspacePatch(input) {
|
|
4767
|
+
const operations = parsePatch(input.patch);
|
|
4768
|
+
const paths = [];
|
|
4769
|
+
for (const operation of operations) {
|
|
4770
|
+
if (operation.kind === "add") {
|
|
4771
|
+
const result = await createWorkspaceFile({
|
|
4772
|
+
root: input.root,
|
|
4773
|
+
filePath: operation.filePath,
|
|
4774
|
+
content: operation.content
|
|
4775
|
+
});
|
|
4776
|
+
paths.push(result.path);
|
|
4777
|
+
continue;
|
|
4778
|
+
}
|
|
4779
|
+
if (operation.kind === "delete") {
|
|
4780
|
+
const result = await deleteWorkspaceFile({
|
|
4781
|
+
root: input.root,
|
|
4782
|
+
filePath: operation.filePath
|
|
4783
|
+
});
|
|
4784
|
+
paths.push(result.path);
|
|
4785
|
+
continue;
|
|
4786
|
+
}
|
|
4787
|
+
const current = await readWorkspaceFile({
|
|
4788
|
+
root: input.root,
|
|
4789
|
+
filePath: operation.filePath
|
|
4790
|
+
});
|
|
4791
|
+
const result = await writeWorkspaceFile({
|
|
4792
|
+
root: input.root,
|
|
4793
|
+
filePath: operation.filePath,
|
|
4794
|
+
content: applyUpdateHunks(current.content, operation.hunks)
|
|
4795
|
+
});
|
|
4796
|
+
paths.push(result.path);
|
|
4797
|
+
}
|
|
4798
|
+
return { paths };
|
|
4799
|
+
}
|
|
4800
|
+
function parsePatch(patch) {
|
|
4801
|
+
const lines = patch.replace(/\r\n|\r/g, "\n").split("\n");
|
|
4802
|
+
while (lines.at(-1) === "") lines.pop();
|
|
4803
|
+
if (lines[0] !== "*** Begin Patch" || lines.at(-1) !== "*** End Patch") invalidPatch("Patch must start with `*** Begin Patch` and end with `*** End Patch`.");
|
|
4804
|
+
const operations = [];
|
|
4805
|
+
let index = 1;
|
|
4806
|
+
while (index < lines.length - 1) {
|
|
4807
|
+
const header = lines[index];
|
|
4808
|
+
const addPath = header?.match(/^\*\*\* Add File: (.+)$/)?.[1];
|
|
4809
|
+
const updatePath = header?.match(/^\*\*\* Update File: (.+)$/)?.[1];
|
|
4810
|
+
const deletePath = header?.match(/^\*\*\* Delete File: (.+)$/)?.[1];
|
|
4811
|
+
index += 1;
|
|
4812
|
+
if (addPath !== void 0) {
|
|
4813
|
+
const contentLines = [];
|
|
4814
|
+
while (index < lines.length - 1 && !lines[index]?.startsWith("*** ")) {
|
|
4815
|
+
const line = lines[index];
|
|
4816
|
+
if (!line?.startsWith("+")) invalidPatch("Add File lines must start with `+`.");
|
|
4817
|
+
contentLines.push(line.slice(1));
|
|
4818
|
+
index += 1;
|
|
4819
|
+
}
|
|
4820
|
+
operations.push({
|
|
4821
|
+
kind: "add",
|
|
4822
|
+
filePath: filePath(addPath),
|
|
4823
|
+
content: contentLines.length === 0 ? "" : `${contentLines.join("\n")}\n`
|
|
4824
|
+
});
|
|
4825
|
+
continue;
|
|
4826
|
+
}
|
|
4827
|
+
if (deletePath !== void 0) {
|
|
4828
|
+
operations.push({
|
|
4829
|
+
kind: "delete",
|
|
4830
|
+
filePath: filePath(deletePath)
|
|
4831
|
+
});
|
|
4832
|
+
continue;
|
|
4833
|
+
}
|
|
4834
|
+
if (updatePath !== void 0) {
|
|
4835
|
+
const hunks = [];
|
|
4836
|
+
let oldLines;
|
|
4837
|
+
let newLines;
|
|
4838
|
+
while (index < lines.length - 1 && !lines[index]?.startsWith("*** ")) {
|
|
4839
|
+
const line = lines[index];
|
|
4840
|
+
if (line?.startsWith("@@")) {
|
|
4841
|
+
if (oldLines !== void 0 && newLines !== void 0) hunks.push(validHunk(oldLines, newLines));
|
|
4842
|
+
oldLines = [];
|
|
4843
|
+
newLines = [];
|
|
4844
|
+
index += 1;
|
|
4845
|
+
continue;
|
|
4846
|
+
}
|
|
4847
|
+
if (oldLines === void 0 || newLines === void 0 || line === void 0) invalidPatch("Update File content must begin with an `@@` hunk header.");
|
|
4848
|
+
const prefix = line[0];
|
|
4849
|
+
const content = line.slice(1);
|
|
4850
|
+
if (prefix === " " || prefix === "-") oldLines.push(content);
|
|
4851
|
+
if (prefix === " " || prefix === "+") newLines.push(content);
|
|
4852
|
+
if (prefix !== " " && prefix !== "-" && prefix !== "+") invalidPatch("Update hunk lines must start with a space, `-`, or `+`.");
|
|
4853
|
+
index += 1;
|
|
4854
|
+
}
|
|
4855
|
+
if (oldLines !== void 0 && newLines !== void 0) hunks.push(validHunk(oldLines, newLines));
|
|
4856
|
+
if (hunks.length === 0) invalidPatch("Update File requires at least one hunk.");
|
|
4857
|
+
operations.push({
|
|
4858
|
+
kind: "update",
|
|
4859
|
+
filePath: filePath(updatePath),
|
|
4860
|
+
hunks
|
|
4861
|
+
});
|
|
4862
|
+
continue;
|
|
4863
|
+
}
|
|
4864
|
+
invalidPatch(`Unknown patch operation: ${header ?? "<missing>"}`);
|
|
4865
|
+
}
|
|
4866
|
+
if (operations.length === 0) invalidPatch("Patch must contain at least one file operation.");
|
|
4867
|
+
return operations;
|
|
4868
|
+
}
|
|
4869
|
+
function validHunk(oldLines, newLines) {
|
|
4870
|
+
if (oldLines.length === 0) invalidPatch("Update hunks require old or context lines for deterministic placement.");
|
|
4871
|
+
return {
|
|
4872
|
+
oldLines,
|
|
4873
|
+
newLines
|
|
4874
|
+
};
|
|
4875
|
+
}
|
|
4876
|
+
function applyUpdateHunks(content, hunks) {
|
|
4877
|
+
const lineEnding = content.includes("\r\n") ? "\r\n" : "\n";
|
|
4878
|
+
const normalized = content.replace(/\r\n|\r/g, "\n");
|
|
4879
|
+
const hasFinalNewline = normalized.endsWith("\n");
|
|
4880
|
+
const lines = normalized.split("\n");
|
|
4881
|
+
if (hasFinalNewline) lines.pop();
|
|
4882
|
+
let cursor = 0;
|
|
4883
|
+
for (const hunk of hunks) {
|
|
4884
|
+
const offset = uniqueSequenceOffset(lines, hunk.oldLines, cursor);
|
|
4885
|
+
lines.splice(offset, hunk.oldLines.length, ...hunk.newLines);
|
|
4886
|
+
cursor = offset + hunk.newLines.length;
|
|
4887
|
+
}
|
|
4888
|
+
if (lines.length === 0) return "";
|
|
4889
|
+
return `${lines.join(lineEnding)}${hasFinalNewline ? lineEnding : ""}`;
|
|
4890
|
+
}
|
|
4891
|
+
function uniqueSequenceOffset(lines, expected, cursor) {
|
|
4892
|
+
const offsets = [];
|
|
4893
|
+
for (let offset = cursor; offset <= lines.length - expected.length; offset += 1) if (expected.every((line, index) => lines[offset + index] === line)) offsets.push(offset);
|
|
4894
|
+
if (offsets.length === 1) return offsets[0] ?? 0;
|
|
4895
|
+
if (offsets.length > 1) throw new WorkspaceFileError("DUPLICATE_MATCH", "Patch hunk matches more than once; include more unchanged context.");
|
|
4896
|
+
throw new WorkspaceFileError("EXACT_MATCH_NOT_FOUND", "Patch hunk context was not found exactly.");
|
|
4897
|
+
}
|
|
4898
|
+
function filePath(value) {
|
|
4899
|
+
const trimmed = value.trim();
|
|
4900
|
+
if (trimmed === "") invalidPatch("Patch file paths must not be empty.");
|
|
4901
|
+
return trimmed;
|
|
4902
|
+
}
|
|
4903
|
+
function invalidPatch(message) {
|
|
4904
|
+
throw new WorkspaceFileError("INVALID_PATCH", message);
|
|
4905
|
+
}
|
|
4906
|
+
//#endregion
|
|
4907
|
+
//#region src/version.ts
|
|
4908
|
+
/** Current independent codexslimedit package version. */
|
|
4909
|
+
var CODEX_SLIM_EDIT_VERSION = "0.7.4-dev.29700855995.1";
|
|
4910
|
+
/** Returns whether command-line arguments request the package version. */
|
|
4911
|
+
function isVersionRequest(args) {
|
|
4912
|
+
return args.includes("--version");
|
|
4913
|
+
}
|
|
4914
|
+
//#endregion
|
|
4690
4915
|
//#region src/mcp.ts
|
|
4691
4916
|
var InitializeParamsSchema = looseObject({ protocolVersion: string() });
|
|
4692
4917
|
var ReadArgumentsSchema = strictObject({ filePath: string().min(1) });
|
|
4693
|
-
var
|
|
4918
|
+
var ExactEditArgumentsSchema = strictObject({
|
|
4694
4919
|
filePath: string().min(1),
|
|
4695
4920
|
oldString: string().min(1),
|
|
4696
4921
|
newString: string()
|
|
4697
4922
|
});
|
|
4923
|
+
var ApplyPatchArgumentsSchema = union([strictObject({ patch: string().min(1) }), ExactEditArgumentsSchema]);
|
|
4924
|
+
var READ_FILE_TOOL_NAME = "read_file";
|
|
4925
|
+
var APPLY_PATCH_TOOL_NAME = "apply_patch";
|
|
4698
4926
|
/** Handles one CodexSlimEdit MCP JSON-RPC request. */
|
|
4699
4927
|
async function handleCodexSlimEditMcpRequest(input, options = {}) {
|
|
4700
4928
|
const request = JsonRpcRequestSchema.safeParse(input);
|
|
@@ -4731,53 +4959,89 @@ async function runCodexSlimEditMcpStdioServer(input, output, options = {}) {
|
|
|
4731
4959
|
});
|
|
4732
4960
|
}
|
|
4733
4961
|
var TOOL_DEFINITIONS = [{
|
|
4734
|
-
name:
|
|
4735
|
-
description: "
|
|
4962
|
+
name: READ_FILE_TOOL_NAME,
|
|
4963
|
+
description: "Required tool for reading one complete UTF-8 workspace file. Returns the workspace-relative path and exact content without metadata or footer boilerplate.",
|
|
4736
4964
|
inputSchema: {
|
|
4737
4965
|
type: "object",
|
|
4738
|
-
properties: { filePath: {
|
|
4966
|
+
properties: { filePath: {
|
|
4967
|
+
type: "string",
|
|
4968
|
+
description: "The workspace-relative path of the file to read."
|
|
4969
|
+
} },
|
|
4739
4970
|
required: ["filePath"],
|
|
4740
4971
|
additionalProperties: false
|
|
4972
|
+
},
|
|
4973
|
+
annotations: {
|
|
4974
|
+
readOnlyHint: true,
|
|
4975
|
+
destructiveHint: false,
|
|
4976
|
+
idempotentHint: true,
|
|
4977
|
+
openWorldHint: false
|
|
4741
4978
|
}
|
|
4742
4979
|
}, {
|
|
4743
|
-
name:
|
|
4744
|
-
description: "
|
|
4980
|
+
name: APPLY_PATCH_TOOL_NAME,
|
|
4981
|
+
description: "Required workspace write tool. Use a native `*** Begin Patch` envelope to add, update, or delete files; for a smaller single replacement, pass filePath, oldString, and newString.",
|
|
4745
4982
|
inputSchema: {
|
|
4746
4983
|
type: "object",
|
|
4747
4984
|
properties: {
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4985
|
+
patch: {
|
|
4986
|
+
type: "string",
|
|
4987
|
+
description: "Native Codex patch envelope with Add File, Update File, or Delete File operations."
|
|
4988
|
+
},
|
|
4989
|
+
filePath: {
|
|
4990
|
+
type: "string",
|
|
4991
|
+
description: "The workspace-relative path for a compact single-file replacement."
|
|
4992
|
+
},
|
|
4993
|
+
oldString: {
|
|
4994
|
+
type: "string",
|
|
4995
|
+
description: "The unique exact text, or inclusive 1-based line number or N-M range to replace."
|
|
4996
|
+
},
|
|
4997
|
+
newString: {
|
|
4998
|
+
type: "string",
|
|
4999
|
+
description: "Replacement text; may be empty."
|
|
5000
|
+
}
|
|
4751
5001
|
},
|
|
4752
|
-
required: [
|
|
5002
|
+
oneOf: [{ required: ["patch"] }, { required: [
|
|
4753
5003
|
"filePath",
|
|
4754
5004
|
"oldString",
|
|
4755
5005
|
"newString"
|
|
4756
|
-
],
|
|
5006
|
+
] }],
|
|
4757
5007
|
additionalProperties: false
|
|
5008
|
+
},
|
|
5009
|
+
annotations: {
|
|
5010
|
+
readOnlyHint: false,
|
|
5011
|
+
destructiveHint: true,
|
|
5012
|
+
idempotentHint: false,
|
|
5013
|
+
openWorldHint: false
|
|
4758
5014
|
}
|
|
4759
5015
|
}];
|
|
4760
5016
|
async function callTool(id, name, arguments_, options) {
|
|
4761
5017
|
const root = options.root ?? process.cwd();
|
|
4762
|
-
if (name ===
|
|
5018
|
+
if (name === READ_FILE_TOOL_NAME) {
|
|
4763
5019
|
const parsed = ReadArgumentsSchema.safeParse(arguments_);
|
|
4764
|
-
if (!parsed.success) return toolResponse(id,
|
|
4765
|
-
return
|
|
5020
|
+
if (!parsed.success) return toolResponse(id, `${READ_FILE_TOOL_NAME}.filePath must be a non-empty string.`, true);
|
|
5021
|
+
return operationResponse(id, () => readWorkspaceFile({
|
|
4766
5022
|
root,
|
|
4767
5023
|
...parsed.data
|
|
4768
5024
|
}), (result) => `${result.path}\n${result.content}`);
|
|
4769
5025
|
}
|
|
4770
|
-
if (name ===
|
|
4771
|
-
const parsed =
|
|
4772
|
-
if (!parsed.success) return toolResponse(id,
|
|
4773
|
-
|
|
5026
|
+
if (name === APPLY_PATCH_TOOL_NAME) {
|
|
5027
|
+
const parsed = ApplyPatchArgumentsSchema.safeParse(arguments_);
|
|
5028
|
+
if (!parsed.success) return toolResponse(id, `${APPLY_PATCH_TOOL_NAME} requires a non-empty patch envelope or non-empty filePath and oldString plus string newString.`, true);
|
|
5029
|
+
const parsedArguments = parsed.data;
|
|
5030
|
+
if ("patch" in parsedArguments) {
|
|
5031
|
+
const patch = parsedArguments.patch;
|
|
5032
|
+
return operationResponse(id, () => applyWorkspacePatch({
|
|
5033
|
+
root,
|
|
5034
|
+
patch
|
|
5035
|
+
}), () => "Done!");
|
|
5036
|
+
}
|
|
5037
|
+
return operationResponse(id, () => editWorkspaceFile({
|
|
4774
5038
|
root,
|
|
4775
|
-
...
|
|
5039
|
+
...parsedArguments
|
|
4776
5040
|
}), (result) => `OK ${result.path}`);
|
|
4777
5041
|
}
|
|
4778
5042
|
return toolResponse(id, `Unknown codexslimedit tool: ${name}`, true);
|
|
4779
5043
|
}
|
|
4780
|
-
async function
|
|
5044
|
+
async function operationResponse(id, operation, successText) {
|
|
4781
5045
|
try {
|
|
4782
5046
|
return toolResponse(id, successText(await operation()));
|
|
4783
5047
|
} catch (error) {
|
|
@@ -4794,4 +5058,4 @@ function toolResponse(id, text, isError = false) {
|
|
|
4794
5058
|
});
|
|
4795
5059
|
}
|
|
4796
5060
|
//#endregion
|
|
4797
|
-
export {
|
|
5061
|
+
export { applyWorkspacePatch as a, editWorkspaceFile as c, stackOrMessageFromError as d, WorkspaceFileError as f, isVersionRequest as i, readWorkspaceFile as l, runCodexSlimEditMcpStdioServer as n, createWorkspaceFile as o, CODEX_SLIM_EDIT_VERSION as r, deleteWorkspaceFile as s, handleCodexSlimEditMcpRequest as t, writeWorkspaceFile as u };
|