@akira-tl/forgerelay 0.2.1 → 0.2.3
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 +20 -0
- package/dist/file-mutations.js +50 -0
- package/dist/mcp/server-instructions.js +15 -6
- package/dist/server.js +145 -2
- 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 +12 -1
- package/docs/configuration.md +3 -3
- package/docs/debugging.md +4 -4
- package/docs/security.md +15 -0
- package/package.json +2 -2
- package/scripts/debug/accept.mjs +41 -2
package/scripts/debug/accept.mjs
CHANGED
|
@@ -83,6 +83,11 @@ try {
|
|
|
83
83
|
assert.equal(initialized.message.result.serverInfo.name, "forgerelay");
|
|
84
84
|
assert.equal(initialized.message.result.serverInfo.title, "ForgeRelay");
|
|
85
85
|
assert.equal(initialized.message.result.serverInfo.version, packageJson.version);
|
|
86
|
+
const serverInstructions = initialized.message.result.instructions ?? "";
|
|
87
|
+
assert.match(serverInstructions, /Shell commands may modify ordinary project files/);
|
|
88
|
+
assert.match(serverInstructions, /\/etc\/sudoers/);
|
|
89
|
+
assert.match(serverInstructions, /configuration files through shell only when the user's request explicitly calls for that configuration change/);
|
|
90
|
+
assert.doesNotMatch(serverInstructions, /Do not create or modify files with bash/);
|
|
86
91
|
pass(
|
|
87
92
|
"MCP initialize",
|
|
88
93
|
JSON.stringify(initialized.message.result.serverInfo),
|
|
@@ -103,9 +108,13 @@ try {
|
|
|
103
108
|
params: {},
|
|
104
109
|
}).message.result.tools;
|
|
105
110
|
const toolNames = tools.map((tool) => tool.name);
|
|
106
|
-
for (const expected of ["open_workspace", "close_worktree", "read", "write", "edit", "grep", "glob", "ls", "bash"]) {
|
|
111
|
+
for (const expected of ["open_workspace", "close_worktree", "read", "write", "edit", "rename", "delete", "grep", "glob", "ls", "bash"]) {
|
|
107
112
|
assert.ok(toolNames.includes(expected), `missing debug tool ${expected}`);
|
|
108
113
|
}
|
|
114
|
+
const bashTool = tools.find((tool) => tool.name === "bash");
|
|
115
|
+
assert.match(bashTool?.description ?? "", /may modify ordinary project files/);
|
|
116
|
+
assert.match(bashTool?.description ?? "", /\/etc\/sudoers/);
|
|
117
|
+
assert.doesNotMatch(bashTool?.description ?? "", /Do not use bash to create, move, rename, or delete project files/);
|
|
109
118
|
pass("MCP tools/list", `${toolNames.length} tools: ${toolNames.join(", ")}`);
|
|
110
119
|
|
|
111
120
|
const opened = callTool(oauth.accessToken, sessionId, 3, "open_workspace", {
|
|
@@ -146,6 +155,21 @@ try {
|
|
|
146
155
|
assert.equal(failedEdit.isError, true);
|
|
147
156
|
pass("failed tool path", "edit returned isError=true and triggered AfterToolFailure");
|
|
148
157
|
|
|
158
|
+
const renamedWorkspaceFile = callTool(oauth.accessToken, sessionId, 74, "rename", {
|
|
159
|
+
workspaceId,
|
|
160
|
+
path: "acceptance.txt",
|
|
161
|
+
newPath: "renamed-acceptance.txt",
|
|
162
|
+
});
|
|
163
|
+
assert.equal(renamedWorkspaceFile.isError, undefined);
|
|
164
|
+
assert.equal(readFileSync(join(checkoutWorkspace, "renamed-acceptance.txt"), "utf8"), "forgerelay 7677 acceptance\n");
|
|
165
|
+
const deletedWorkspaceFile = callTool(oauth.accessToken, sessionId, 75, "delete", {
|
|
166
|
+
workspaceId,
|
|
167
|
+
path: "renamed-acceptance.txt",
|
|
168
|
+
});
|
|
169
|
+
assert.equal(deletedWorkspaceFile.isError, undefined);
|
|
170
|
+
assert.equal(existsSync(join(checkoutWorkspace, "renamed-acceptance.txt")), false);
|
|
171
|
+
pass("rename + delete", "workspace file renamed and deleted through MCP");
|
|
172
|
+
|
|
149
173
|
mkdirSync(tempAcceptanceRoot, { recursive: true });
|
|
150
174
|
const tempFile = join(tempAcceptanceRoot, "mcp-temp.txt");
|
|
151
175
|
const tempWritten = callTool(oauth.accessToken, sessionId, 70, "write", {
|
|
@@ -169,13 +193,28 @@ try {
|
|
|
169
193
|
assert.equal(tempEdited.isError, undefined);
|
|
170
194
|
assert.equal(readFileSync(tempFile, "utf8"), "forgerelay temp after edit\n");
|
|
171
195
|
|
|
196
|
+
const renamedTempFile = join(tempAcceptanceRoot, "mcp-temp-renamed.txt");
|
|
197
|
+
const tempRenamed = callTool(oauth.accessToken, sessionId, 76, "rename", {
|
|
198
|
+
workspaceId,
|
|
199
|
+
path: tempFile,
|
|
200
|
+
newPath: renamedTempFile,
|
|
201
|
+
});
|
|
202
|
+
assert.equal(tempRenamed.isError, undefined);
|
|
203
|
+
assert.equal(readFileSync(renamedTempFile, "utf8"), "forgerelay temp after edit\n");
|
|
204
|
+
const tempDeleted = callTool(oauth.accessToken, sessionId, 77, "delete", {
|
|
205
|
+
workspaceId,
|
|
206
|
+
path: renamedTempFile,
|
|
207
|
+
});
|
|
208
|
+
assert.equal(tempDeleted.isError, undefined);
|
|
209
|
+
assert.equal(existsSync(renamedTempFile), false);
|
|
210
|
+
|
|
172
211
|
const outsideRoots = callTool(oauth.accessToken, sessionId, 73, "read", {
|
|
173
212
|
workspaceId,
|
|
174
213
|
path: join(homedir(), "forgerelay-debug-outside-roots.txt"),
|
|
175
214
|
});
|
|
176
215
|
assert.equal(outsideRoots.isError, true);
|
|
177
216
|
assert.match(toolText(outsideRoots), /outside allowed roots/i);
|
|
178
|
-
pass("OS temp file tools", "write + read + edit passed; arbitrary home path rejected");
|
|
217
|
+
pass("OS temp file tools", "write + read + edit + rename + delete passed; arbitrary home path rejected");
|
|
179
218
|
|
|
180
219
|
setupGitProject(gitProject);
|
|
181
220
|
const worktreeOpened = callTool(oauth.accessToken, sessionId, 8, "open_workspace", {
|