@akira-tl/forgerelay 0.2.1 → 0.2.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.
- package/CHANGELOG.md +10 -0
- package/dist/file-mutations.js +50 -0
- package/dist/mcp/server-instructions.js +9 -5
- package/dist/server.js +143 -0
- package/dist/ui/.vite/manifest.json +280 -280
- package/dist/ui/assets/{angular-html-CovppXt5.js → angular-html-CQmCUfkv.js} +1 -1
- package/dist/ui/assets/{angular-ts-0Y9l6YXu.js → angular-ts-BSbfxpMS.js} +1 -1
- package/dist/ui/assets/{apl-BmBhiklA.js → apl-CWCaH-E3.js} +1 -1
- package/dist/ui/assets/{astro-BAmqcqyV.js → astro-R_p4G0xA.js} +1 -1
- package/dist/ui/assets/{blade-BjFcmRJo.js → blade-irHensDY.js} +1 -1
- package/dist/ui/assets/{c-CaOJn4x0.js → c-_XEQUoq6.js} +1 -1
- package/dist/ui/assets/{cobol-DR4zWFMt.js → cobol-Css6VQjJ.js} +1 -1
- package/dist/ui/assets/{coffee-ReGSThmM.js → coffee-CpXUkyDm.js} +1 -1
- package/dist/ui/assets/{cpp-CnlQh6MO.js → cpp-BQHsPWwI.js} +1 -1
- package/dist/ui/assets/{crystal-BrpPbH1U.js → crystal-Cbv8J9Fm.js} +1 -1
- package/dist/ui/assets/{css-DRBEvyPU.js → css-D8DkrS3U.js} +1 -1
- package/dist/ui/assets/{edge-cfwpdvl9.js → edge-bBL_XDAG.js} +1 -1
- package/dist/ui/assets/{elixir-7owrJxb6.js → elixir-CQjsM9sA.js} +1 -1
- package/dist/ui/assets/{elm-udiOjODk.js → elm-DLFl-jEZ.js} +1 -1
- package/dist/ui/assets/{erb-8ctXHMkS.js → erb-BJjsdB_Q.js} +1 -1
- package/dist/ui/assets/{git-rebase-P0V_qK1w.js → git-rebase-CqP5A2s0.js} +1 -1
- package/dist/ui/assets/{glimmer-js-CZ14ED7U.js → glimmer-js-B5Hh_7xe.js} +1 -1
- package/dist/ui/assets/{glimmer-ts-DtBSFP-o.js → glimmer-ts-oaeSAC8s.js} +1 -1
- package/dist/ui/assets/{glsl-BX2BkZJK.js → glsl-BylkOpcF.js} +1 -1
- package/dist/ui/assets/{graphql-Dj_cPv4w.js → graphql-DsVRvP6S.js} +1 -1
- package/dist/ui/assets/{hack-DGRkd8xn.js → hack-D4N5abT_.js} +1 -1
- package/dist/ui/assets/{haml-BAPSvujW.js → haml-CIAJQIox.js} +1 -1
- package/dist/ui/assets/{handlebars-PYksdULG.js → handlebars-DDEZyNXR.js} +1 -1
- package/dist/ui/assets/{heavy-payload-BjE0LSUi.js → heavy-payload-DafM8yjB.js} +1 -1
- package/dist/ui/assets/{html-derivative-C6q86ZrS.js → html-derivative-CZ91Y2d5.js} +1 -1
- package/dist/ui/assets/{html-DloAPGZ1.js → html-zDy-3lvy.js} +1 -1
- package/dist/ui/assets/{http-fPvYDvHA.js → http-ezaodZRf.js} +1 -1
- package/dist/ui/assets/{hurl-Dbir4WvE.js → hurl-BmL0o7TO.js} +1 -1
- package/dist/ui/assets/{java-DvkqXg3z.js → java-Q2C0vwbH.js} +1 -1
- package/dist/ui/assets/{javascript-TONTV6Ty.js → javascript-UDhBkHE3.js} +1 -1
- package/dist/ui/assets/{jinja-OM1F5Gct.js → jinja-BWRskcYS.js} +1 -1
- package/dist/ui/assets/{jison-CBttugts.js → jison-B18BY9Ds.js} +1 -1
- package/dist/ui/assets/{json-Db5oY-7J.js → json-DNj7MpRr.js} +1 -1
- package/dist/ui/assets/{jsx-DKeJCgsc.js → jsx-CVpHJ6cr.js} +1 -1
- package/dist/ui/assets/{julia-NlIPhXaL.js → julia-CJBNBS9c.js} +1 -1
- package/dist/ui/assets/{just-BXBJB-mX.js → just-Bmu2JCQT.js} +1 -1
- package/dist/ui/assets/{latex-CGK-6yDy.js → latex-BB7F93KY.js} +1 -1
- package/dist/ui/assets/{liquid-D7waQ0MY.js → liquid-Btnomc6E.js} +1 -1
- package/dist/ui/assets/{lua-C_kkt4h1.js → lua-D0FYT9Vo.js} +1 -1
- package/dist/ui/assets/{marko-BokXuCts.js → marko-MHLw0ZlK.js} +1 -1
- package/dist/ui/assets/{mdc-Bk0nnUJA.js → mdc-CSr-suGy.js} +1 -1
- package/dist/ui/assets/{nginx-jM6NF9TV.js → nginx-ur1l6E-g.js} +1 -1
- package/dist/ui/assets/{nim-hYsL-WKb.js → nim-B2k0IugA.js} +1 -1
- package/dist/ui/assets/{perl-DBI_cUHJ.js → perl-BqOHxFDn.js} +1 -1
- package/dist/ui/assets/{php-BX_aQhFl.js → php-DzPK6O1_.js} +1 -1
- package/dist/ui/assets/{pug-DwFAYy1e.js → pug-D169OZkr.js} +1 -1
- package/dist/ui/assets/{qml-eLSwQUKt.js → qml-BtemJTC4.js} +1 -1
- package/dist/ui/assets/{r-BMnQsMLf.js → r-BGVHu0bd.js} +1 -1
- package/dist/ui/assets/{razor-CgciiMyw.js → razor-Diisjg1d.js} +1 -1
- package/dist/ui/assets/{regexp-ZrURNrn5.js → regexp-B2F5vMZ6.js} +1 -1
- package/dist/ui/assets/{review-payload-HGkpJjjm.js → review-payload-B-3I3Oj_.js} +1 -1
- package/dist/ui/assets/{rst-EoAOr3M6.js → rst-BxpeZwuT.js} +1 -1
- package/dist/ui/assets/{ruby-Dqx5_Rqw.js → ruby-DrCq4riN.js} +1 -1
- package/dist/ui/assets/{sas-Bf6xPLSX.js → sas-CI_2W-1n.js} +1 -1
- package/dist/ui/assets/{scrollbar-DX4Ujotb.js → scrollbar-DHsDF5w-.js} +3 -3
- package/dist/ui/assets/{scss-DDpW3yTd.js → scss-BxtyHZ1p.js} +1 -1
- package/dist/ui/assets/{shellscript-DayAQ-W9.js → shellscript-BY_YYrHT.js} +1 -1
- package/dist/ui/assets/{shellsession-jPr_lSnA.js → shellsession-C6uBrLmA.js} +1 -1
- package/dist/ui/assets/{soy-X9adks8G.js → soy-BHM9gdPh.js} +1 -1
- package/dist/ui/assets/{sql-Bmt09AIR.js → sql-XmhfEGz_.js} +1 -1
- package/dist/ui/assets/{stata-DVkNpH8r.js → stata-BbHY7NEo.js} +1 -1
- package/dist/ui/assets/{surrealql-eEXzMb8j.js → surrealql-CD49DZ3E.js} +1 -1
- package/dist/ui/assets/{svelte-CFoBxZF3.js → svelte-CbYsEYJY.js} +1 -1
- package/dist/ui/assets/{templ-Da_324JF.js → templ-B5JoUxEB.js} +1 -1
- package/dist/ui/assets/{tex-HAzlmKJ0.js → tex-DZcOfTZK.js} +1 -1
- package/dist/ui/assets/{ts-tags-7iLrNH8p.js → ts-tags-BeU-xqc0.js} +1 -1
- package/dist/ui/assets/{tsx-gZaXf8PJ.js → tsx-V7W1xGl3.js} +1 -1
- package/dist/ui/assets/{twig-CLpQ7p1N.js → twig-MIaJo5BU.js} +1 -1
- package/dist/ui/assets/{typescript-B2WWCRI2.js → typescript-C0WESuVc.js} +1 -1
- package/dist/ui/assets/{vue-No_8Jygd.js → vue-eJHwc9ra.js} +1 -1
- package/dist/ui/assets/{vue-html-kHJg98Kk.js → vue-html-CLmBujTU.js} +1 -1
- package/dist/ui/assets/{vue-vine-Df4cEYaN.js → vue-vine-BP5kjIGb.js} +1 -1
- package/dist/ui/assets/{workspace-app-C_wsC6VL.js → workspace-app-C1qR5cAY.js} +3 -3
- package/dist/ui/assets/{xml-DUZ05dpG.js → xml-j7pmyP48.js} +1 -1
- package/dist/ui/assets/{xsl-Bwp3w2OF.js → xsl-5VFHbg6o.js} +1 -1
- package/dist/ui/assets/{yaml-IA_-TEM1.js → yaml-DxFAt-KU.js} +1 -1
- package/dist/ui/workspace-app.html +1 -1
- package/docs/chatgpt-coding-workflow.md +4 -1
- package/docs/configuration.md +3 -3
- package/docs/debugging.md +2 -2
- package/package.json +2 -2
- package/scripts/debug/accept.mjs +32 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable ForgeRelay changes are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.2] - 2026-08-09
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- First-class `rename` and `delete` MCP tools for files and directories in workspace or OS-temp file roots, including direct availability in minimal, full, and Codex tool modes.
|
|
12
|
+
|
|
13
|
+
### Security
|
|
14
|
+
|
|
15
|
+
- `rename` validates both source and destination canonical roots and refuses existing destinations; `delete` refuses allowed roots themselves and requires explicit `recursive: true` for non-empty directory trees.
|
|
16
|
+
|
|
7
17
|
## [0.2.1] - 2026-08-09
|
|
8
18
|
|
|
9
19
|
### Changed
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { lstat, realpath, rename, rm, rmdir, unlink } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { expandHomePath, resolveCanonicalAllowedPath } from "./roots.js";
|
|
4
|
+
export async function renamePath(input, context) {
|
|
5
|
+
const source = await resolveCanonicalAllowedPath(input.path, context.cwd, context.allowedRoots);
|
|
6
|
+
await assertNotAllowedRootItself(source, context.allowedRoots, input.path);
|
|
7
|
+
const destination = await resolveCanonicalAllowedPath(input.newPath, context.cwd, context.allowedRoots);
|
|
8
|
+
await assertDestinationMissing(destination, input.newPath);
|
|
9
|
+
await lstat(source);
|
|
10
|
+
await rename(source, destination);
|
|
11
|
+
return { path: input.path, newPath: input.newPath };
|
|
12
|
+
}
|
|
13
|
+
export async function deletePath(input, context) {
|
|
14
|
+
const path = await resolveCanonicalAllowedPath(input.path, context.cwd, context.allowedRoots);
|
|
15
|
+
await assertNotAllowedRootItself(path, context.allowedRoots, input.path);
|
|
16
|
+
const entry = await lstat(path);
|
|
17
|
+
const recursive = input.recursive ?? false;
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
if (recursive) {
|
|
20
|
+
await rm(path, { recursive: true, force: false });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
await rmdir(path);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
await unlink(path);
|
|
28
|
+
}
|
|
29
|
+
return { path: input.path, recursive };
|
|
30
|
+
}
|
|
31
|
+
async function assertDestinationMissing(path, inputPath) {
|
|
32
|
+
try {
|
|
33
|
+
await lstat(path);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error.code === "ENOENT")
|
|
37
|
+
return;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Rename destination already exists: ${inputPath}`);
|
|
41
|
+
}
|
|
42
|
+
async function assertNotAllowedRootItself(path, allowedRoots, inputPath) {
|
|
43
|
+
const canonicalPath = await realpath(path);
|
|
44
|
+
for (const root of allowedRoots) {
|
|
45
|
+
const canonicalRoot = await realpath(resolve(expandHomePath(root)));
|
|
46
|
+
if (canonicalPath === canonicalRoot) {
|
|
47
|
+
throw new Error(`Cannot rename or delete an allowed root itself: ${inputPath}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -4,6 +4,8 @@ export const toolNames = {
|
|
|
4
4
|
read: "read",
|
|
5
5
|
write: "write",
|
|
6
6
|
edit: "edit",
|
|
7
|
+
rename: "rename",
|
|
8
|
+
delete: "delete",
|
|
7
9
|
grep: "grep",
|
|
8
10
|
glob: "glob",
|
|
9
11
|
ls: "ls",
|
|
@@ -23,15 +25,17 @@ export function buildToolDescriptions(config) {
|
|
|
23
25
|
read: `Read a file inside an open workspace or the OS temp directory. Instruction files returned by ${toolNames.openWorkspace} and advertised skill files are also readable when applicable.${skillCapability} Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
24
26
|
write: `Create or completely overwrite a file inside an open workspace or the OS temp directory. Workspace paths may be relative; OS temp paths may be absolute. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
25
27
|
edit: `Edit one file inside an open workspace or the OS temp directory by replacing exact text blocks. Each oldText must match a unique, non-overlapping region of the original file. Workspace paths may be relative; OS temp paths may be absolute. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
28
|
+
rename: `Rename or move one file or directory inside an open workspace or the OS temp directory without overwriting an existing destination. Source and destination must both remain inside the permitted file roots. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
29
|
+
delete: `Delete one file or directory inside an open workspace or the OS temp directory. Non-empty directories require recursive=true. An allowed root itself cannot be deleted. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
26
30
|
applyPatch: `Apply one Codex-style patch inside an open workspace or the OS temp directory. Supports adding, overwriting, updating, deleting, and moving files. Workspace paths must remain relative; absolute paths are accepted only inside the OS temp directory. Call ${toolNames.openWorkspace} first and pass workspaceId.`,
|
|
27
|
-
shell: `Run a shell command inside an open workspace.${shellSurface} Commands execute with the local user's authority; workspace filesystem containment does not make shell execution a sandbox. Do not use ${toolNames.shell} to create or
|
|
31
|
+
shell: `Run a shell command inside an open workspace.${shellSurface} Commands execute with the local user's authority; workspace filesystem containment does not make shell execution a sandbox. Do not use ${toolNames.shell} to create, move, rename, or delete project files; use ${toolNames.edit}, ${toolNames.write}, ${toolNames.rename}, or ${toolNames.delete} for file changes. Call ${toolNames.openWorkspace} first and pass workspaceId. This capability should only be exposed behind strong authentication.`,
|
|
28
32
|
shellCommand: "Shell command to run with the local user's authority.",
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
function capabilityContractInstructions(config, context) {
|
|
32
36
|
const workspaceLifecycle = config.toolMode === "codex"
|
|
33
37
|
? `Use ForgeRelay as a local coding workspace. Default to the user's existing checkout and reuse its workspaceId. Only open mode=\"worktree\" when the user explicitly asks for isolated or parallel work. Managed worktrees use dedicated forgerelay/* branches, not detached HEADs. When work in a managed worktree is complete and verified, call ${toolNames.closeWorktree}; it commits remaining worktree changes, fast-forwards the original target branch only when safe, then removes the worktree and its branch. If the target branch diverged or the source checkout is dirty, closing is refused and the worktree is preserved.`
|
|
34
|
-
: `Use ForgeRelay as a local coding workspace. Default to the user's existing checkout and reuse its workspaceId for later file, search, edit, write, show-changes, and shell tools. Only open mode=\"worktree\" when the user explicitly asks for isolated or parallel work. Managed worktrees use dedicated forgerelay/* branches, not detached HEADs. When work in a managed worktree is complete and verified, call ${toolNames.closeWorktree}; it commits remaining worktree changes, fast-forwards the original target branch only when safe, then removes the worktree and its branch. If the target branch diverged or the source checkout is dirty, closing is refused and the worktree is preserved.`;
|
|
38
|
+
: `Use ForgeRelay as a local coding workspace. Default to the user's existing checkout and reuse its workspaceId for later file, search, edit, write, rename, delete, show-changes, and shell tools. Only open mode=\"worktree\" when the user explicitly asks for isolated or parallel work. Managed worktrees use dedicated forgerelay/* branches, not detached HEADs. When work in a managed worktree is complete and verified, call ${toolNames.closeWorktree}; it commits remaining worktree changes, fast-forwards the original target branch only when safe, then removes the worktree and its branch. If the target branch diverged or the source checkout is dirty, closing is refused and the worktree is preserved.`;
|
|
35
39
|
const agents = `Follow instructions returned by ${toolNames.openWorkspace}. Before working under a path listed in availableAgentsFiles, use ${toolNames.read} to inspect that instruction file and follow it.`;
|
|
36
40
|
const skills = config.skillsEnabled
|
|
37
41
|
? `When ${toolNames.openWorkspace} returns available skills and a task matches a skill, use ${toolNames.read} to read that skill's path before proceeding. Skill paths may be outside the workspace, but ${toolNames.read} only permits advertised SKILL.md files and files under already-loaded skill directories.`
|
|
@@ -48,7 +52,7 @@ function capabilityContractInstructions(config, context) {
|
|
|
48
52
|
}
|
|
49
53
|
function toolSurfaceInstructions(config) {
|
|
50
54
|
if (config.toolMode === "codex") {
|
|
51
|
-
return `In codex tool mode, workspace file and command operations use ${toolNames.read}, apply_patch, exec_command, and write_stdin.`;
|
|
55
|
+
return `In codex tool mode, workspace file and command operations use ${toolNames.read}, ${toolNames.rename}, ${toolNames.delete}, apply_patch, exec_command, and write_stdin.`;
|
|
52
56
|
}
|
|
53
57
|
if (config.toolMode === "full") {
|
|
54
58
|
return `In full tool mode, dedicated ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} inspection tools are available alongside the core workspace tools.`;
|
|
@@ -64,12 +68,12 @@ function selectedWorkflowInstructions(config) {
|
|
|
64
68
|
}
|
|
65
69
|
function defaultWorkflowInstructions(config) {
|
|
66
70
|
if (config.toolMode === "codex") {
|
|
67
|
-
return `Use ${toolNames.read} for direct file reads, apply_patch for
|
|
71
|
+
return `Use ${toolNames.read} for direct file reads, ${toolNames.rename} and ${toolNames.delete} for direct path moves or removals, apply_patch for content modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes.`;
|
|
68
72
|
}
|
|
69
73
|
const inspection = config.toolMode === "full"
|
|
70
74
|
? `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection.`
|
|
71
75
|
: `Use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection.`;
|
|
72
|
-
return joinInstructions(inspection, `Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell.`, `Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.`);
|
|
76
|
+
return joinInstructions(inspection, `Prefer ${toolNames.edit} for targeted content modifications, ${toolNames.write} only for new files or complete rewrites, ${toolNames.rename} for path moves, ${toolNames.delete} for removals, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell.`, `Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.`);
|
|
73
77
|
}
|
|
74
78
|
function joinInstructions(...parts) {
|
|
75
79
|
return parts
|
package/dist/server.js
CHANGED
|
@@ -14,6 +14,7 @@ import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE, } from "@mode
|
|
|
14
14
|
import express from "express";
|
|
15
15
|
import * as z from "zod/v4";
|
|
16
16
|
import { applyPatch } from "./apply-patch.js";
|
|
17
|
+
import { deletePath, renamePath } from "./file-mutations.js";
|
|
17
18
|
import { isArtifactDownloadSupportedPlatform, registerArtifactTools, } from "./artifact-tools.js";
|
|
18
19
|
import { loadConfig } from "./config.js";
|
|
19
20
|
import { attachHookReports, HookRunner, runToolWithHooks } from "./hooks.js";
|
|
@@ -1069,6 +1070,148 @@ export function createMcpServer(config, workspaces, reviewCheckpoints, processSe
|
|
|
1069
1070
|
});
|
|
1070
1071
|
});
|
|
1071
1072
|
}
|
|
1073
|
+
registerAppTool(server, toolNames.rename, {
|
|
1074
|
+
title: "Rename path",
|
|
1075
|
+
description: toolDescriptions.rename,
|
|
1076
|
+
inputSchema: {
|
|
1077
|
+
workspaceId: z.string().describe("Workspace identifier returned by open_workspace."),
|
|
1078
|
+
path: z.string().describe("Source file or directory path relative to the workspace root, or absolute inside the OS temp directory."),
|
|
1079
|
+
newPath: z.string().describe("Destination path relative to the workspace root, or absolute inside the OS temp directory. The destination must not already exist."),
|
|
1080
|
+
},
|
|
1081
|
+
outputSchema: resultOutputSchema({
|
|
1082
|
+
status: z.literal("renamed"),
|
|
1083
|
+
path: z.string(),
|
|
1084
|
+
newPath: z.string(),
|
|
1085
|
+
}),
|
|
1086
|
+
...toolWidgetDescriptorMeta(config, "edit"),
|
|
1087
|
+
annotations: EDIT_TOOL_ANNOTATIONS,
|
|
1088
|
+
}, async ({ workspaceId, path, newPath }, extra) => {
|
|
1089
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
1090
|
+
return runToolWithHooks(hooks, {
|
|
1091
|
+
tool: toolNames.rename,
|
|
1092
|
+
invocation: workspaceHookInvocation(workspace),
|
|
1093
|
+
payload: { path, newPath, paths: [path, newPath] },
|
|
1094
|
+
changedPaths: () => [path, newPath],
|
|
1095
|
+
operation: async () => {
|
|
1096
|
+
const startedAt = performance.now();
|
|
1097
|
+
try {
|
|
1098
|
+
await renamePath({ path, newPath }, {
|
|
1099
|
+
cwd: workspace.root,
|
|
1100
|
+
allowedRoots: workspaces.fileToolRoots(workspace),
|
|
1101
|
+
});
|
|
1102
|
+
const result = `Renamed ${path} to ${newPath}.`;
|
|
1103
|
+
const content = [textBlock(result)];
|
|
1104
|
+
logToolCall(config, {
|
|
1105
|
+
tool: toolNames.rename,
|
|
1106
|
+
...workspaceLogContext(workspace, extra.sessionId),
|
|
1107
|
+
path: `${path} -> ${newPath}`,
|
|
1108
|
+
success: true,
|
|
1109
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
1110
|
+
});
|
|
1111
|
+
return {
|
|
1112
|
+
content,
|
|
1113
|
+
_meta: {
|
|
1114
|
+
tool: toolNames.rename,
|
|
1115
|
+
card: {
|
|
1116
|
+
workspaceId,
|
|
1117
|
+
path: newPath,
|
|
1118
|
+
summary: { previousPath: path },
|
|
1119
|
+
payload: { content },
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
structuredContent: {
|
|
1123
|
+
result,
|
|
1124
|
+
status: "renamed",
|
|
1125
|
+
path,
|
|
1126
|
+
newPath,
|
|
1127
|
+
},
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
catch (error) {
|
|
1131
|
+
logToolCall(config, {
|
|
1132
|
+
tool: toolNames.rename,
|
|
1133
|
+
...workspaceLogContext(workspace, extra.sessionId),
|
|
1134
|
+
path: `${path} -> ${newPath}`,
|
|
1135
|
+
success: false,
|
|
1136
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
1137
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1138
|
+
});
|
|
1139
|
+
throw error;
|
|
1140
|
+
}
|
|
1141
|
+
},
|
|
1142
|
+
});
|
|
1143
|
+
});
|
|
1144
|
+
registerAppTool(server, toolNames.delete, {
|
|
1145
|
+
title: "Delete path",
|
|
1146
|
+
description: toolDescriptions.delete,
|
|
1147
|
+
inputSchema: {
|
|
1148
|
+
workspaceId: z.string().describe("Workspace identifier returned by open_workspace."),
|
|
1149
|
+
path: z.string().describe("File or directory path relative to the workspace root, or absolute inside the OS temp directory."),
|
|
1150
|
+
recursive: z.boolean().optional().describe("Delete a non-empty directory tree. Defaults to false."),
|
|
1151
|
+
},
|
|
1152
|
+
outputSchema: resultOutputSchema({
|
|
1153
|
+
status: z.literal("deleted"),
|
|
1154
|
+
path: z.string(),
|
|
1155
|
+
recursive: z.boolean(),
|
|
1156
|
+
}),
|
|
1157
|
+
...toolWidgetDescriptorMeta(config, "edit"),
|
|
1158
|
+
annotations: EDIT_TOOL_ANNOTATIONS,
|
|
1159
|
+
}, async ({ workspaceId, path, recursive }, extra) => {
|
|
1160
|
+
const workspace = workspaces.getWorkspace(workspaceId);
|
|
1161
|
+
return runToolWithHooks(hooks, {
|
|
1162
|
+
tool: toolNames.delete,
|
|
1163
|
+
invocation: workspaceHookInvocation(workspace),
|
|
1164
|
+
payload: { path, recursive: recursive ?? false },
|
|
1165
|
+
changedPaths: () => [path],
|
|
1166
|
+
operation: async () => {
|
|
1167
|
+
const startedAt = performance.now();
|
|
1168
|
+
try {
|
|
1169
|
+
const deleted = await deletePath({ path, recursive }, {
|
|
1170
|
+
cwd: workspace.root,
|
|
1171
|
+
allowedRoots: workspaces.fileToolRoots(workspace),
|
|
1172
|
+
});
|
|
1173
|
+
const result = `Deleted ${path}${deleted.recursive ? " recursively" : ""}.`;
|
|
1174
|
+
const content = [textBlock(result)];
|
|
1175
|
+
logToolCall(config, {
|
|
1176
|
+
tool: toolNames.delete,
|
|
1177
|
+
...workspaceLogContext(workspace, extra.sessionId),
|
|
1178
|
+
path,
|
|
1179
|
+
success: true,
|
|
1180
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
1181
|
+
});
|
|
1182
|
+
return {
|
|
1183
|
+
content,
|
|
1184
|
+
_meta: {
|
|
1185
|
+
tool: toolNames.delete,
|
|
1186
|
+
card: {
|
|
1187
|
+
workspaceId,
|
|
1188
|
+
path,
|
|
1189
|
+
summary: { recursive: deleted.recursive },
|
|
1190
|
+
payload: { content },
|
|
1191
|
+
},
|
|
1192
|
+
},
|
|
1193
|
+
structuredContent: {
|
|
1194
|
+
result,
|
|
1195
|
+
status: "deleted",
|
|
1196
|
+
path,
|
|
1197
|
+
recursive: deleted.recursive,
|
|
1198
|
+
},
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
catch (error) {
|
|
1202
|
+
logToolCall(config, {
|
|
1203
|
+
tool: toolNames.delete,
|
|
1204
|
+
...workspaceLogContext(workspace, extra.sessionId),
|
|
1205
|
+
path,
|
|
1206
|
+
success: false,
|
|
1207
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
1208
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1209
|
+
});
|
|
1210
|
+
throw error;
|
|
1211
|
+
}
|
|
1212
|
+
},
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1072
1215
|
if (config.toolMode === "codex") {
|
|
1073
1216
|
registerAppTool(server, "apply_patch", {
|
|
1074
1217
|
title: "Apply patch",
|