Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/chatgpt-coding-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,11 @@ The shell tool is for commands that belong in a terminal:

- tests
- builds
- git inspection
- Git operations, including inspection and `git add`, `git commit`, `git fetch`,
`git pull`, and `git push`
- package scripts
- environment checks

File writes should go through the edit/write tools rather than shell
redirection, heredocs, `tee`, `sed -i`, or generated scripts.
Outside Git operations, project source file writes should go through the
edit/write tools rather than shell redirection, heredocs, `tee`, `sed -i`, or
generated scripts.
37 changes: 35 additions & 2 deletions src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,36 @@ test("open_workspace keeps lifecycle flags out of model output and preserves com
assert.ok(Array.isArray(card.agents));
});

test("bash guidance allows Git writes without authorizing shell-based source edits", async (t) => {
for (const toolMode of ["full", "minimal"] as const) {
await t.test(toolMode, async (t) => {
const context = await fixture(t, { toolMode });
const tools = await context.client.listTools();
const bashTool = tools.tools.find((tool) => tool.name === "bash");
assert.ok(bashTool?.description);

const commandDescription = (
bashTool.inputSchema as {
properties?: { command?: { description?: string } };
}
).properties?.command?.description;
assert.ok(commandDescription);

const instructions = context.client.getInstructions();
assert.ok(instructions);

for (const guidance of [bashTool.description, commandDescription, instructions]) {
for (const command of ["git add", "git commit", "git fetch", "git pull", "git push"]) {
assert.match(guidance, new RegExp(`\\b${command}\\b`));
}
assert.match(guidance, /project source files/);
assert.doesNotMatch(guidance, /Use only for/);
assert.doesNotMatch(guidance, /Must not create or modify project files/);
}
});
}
});

test("concurrent checkout opens return one full context and one reuse instruction", async (t) => {
const context = await fixture(t);
const [first, second] = await Promise.all([
Expand Down Expand Up @@ -175,7 +205,10 @@ interface ServerFixture {
close: () => Promise<void>;
}

async function fixture(t: TestContext, options: { git?: boolean } = {}): Promise<ServerFixture> {
async function fixture(
t: TestContext,
options: { git?: boolean; toolMode?: "full" | "minimal" } = {},
): Promise<ServerFixture> {
const root = await mkdtemp(join(tmpdir(), "devspace-server-test-"));
const project = join(root, "project");
const agentDir = join(root, "agent");
Expand Down Expand Up @@ -209,7 +242,7 @@ async function fixture(t: TestContext, options: { git?: boolean } = {}): Promise
DEVSPACE_WORKTREE_ROOT: join(root, ".worktrees"),
DEVSPACE_AGENT_DIR: agentDir,
DEVSPACE_WIDGETS: "full",
DEVSPACE_TOOL_MODE: "full",
DEVSPACE_TOOL_MODE: options.toolMode ?? "full",
DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough",
PORT: "1",
});
Expand Down
8 changes: 4 additions & 4 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ function serverInstructions(config: ServerConfig): string {

const agentsMd = `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. `;

return `Use DevSpace for coding work. Call ${toolNames.openWorkspace} once for each project folder or isolated worktree, then keep using its workspaceId. During continued work in the same project or worktree, do not call ${toolNames.openWorkspace} again. Open another workspace only when changing projects, switching checkout/worktree mode, creating another isolated worktree, or when the current workspaceId is rejected. ${agentsMd}${skills}${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.${artifactInstruction}${showChangesInstruction}`;
return `Use DevSpace for coding work. Call ${toolNames.openWorkspace} once for each project folder or isolated worktree, then keep using its workspaceId. During continued work in the same project or worktree, do not call ${toolNames.openWorkspace} again. Open another workspace only when changing projects, switching checkout/worktree mode, creating another isolated worktree, or when the current workspaceId is rejected. ${agentsMd}${skills}${inspection}Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, package scripts, Git operations, and commands that are better executed by the shell. Git read and write operations such as git status, git diff, git add, git commit, git fetch, git pull, and git push are allowed. Outside Git operations, do not create or modify project source 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 source files.${artifactInstruction}${showChangesInstruction}`;
}

function formatVisibleAgent(agent: {
Expand Down Expand Up @@ -1561,16 +1561,16 @@ export function createMcpServer(
{
title: "Bash",
description: config.toolMode !== "full"
? `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`
: `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. This is powerful execution and should only be exposed behind strong authentication.`,
? `Run a shell command in a workspace for tests, builds, package scripts, Git operations, search, file discovery, directory inspection, and other shell-side tooling. Git read and write operations such as git status, git diff, git add, git commit, git fetch, git pull, and git push are allowed. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Outside Git operations, do not use ${toolNames.shell} to create or modify project source files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project source files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`
: `Run a shell command in a workspace for tests, builds, package scripts, Git operations, and commands that are better executed by the shell. Git read and write operations such as git status, git diff, git add, git commit, git fetch, git pull, and git push are allowed. Outside Git operations, do not use ${toolNames.shell} to create or modify project source files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project source files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. This is powerful execution and should only be exposed behind strong authentication.`,
Comment thread
teddyli18000 marked this conversation as resolved.
Outdated
inputSchema: {
workspaceId: z
.string()
.describe(workspaceIdDescription),
command: z
.string()
.describe(
`Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`,
`Shell command to run. Git read and write operations such as git status, git diff, git add, git commit, git fetch, git pull, and git push are allowed. Outside Git operations, must not create or modify project source files; use ${toolNames.edit} or ${toolNames.write} for source file changes.`,
),
workingDirectory: z
.string()
Expand Down