summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlcan <[email protected]>2025-04-24 18:03:33 -0700
committerGitHub <[email protected]>2025-04-24 18:03:33 -0700
commitcbba8007b242f7212ff5bcce618c569b25235024 (patch)
tree684cb05c218d33df6650deff9c622ba7b4f7f8b5
parenta94a9ce3bf7bc015ebbd679d0d95be1c5d9762c2 (diff)
shell bones (#160)
* shell bones * Merge remote-tracking branch 'origin/main' into shell_bones * add line break * another line break * drop the log to avoid breaking terminals * rename tool to be consistent with terminal * fix build
-rw-r--r--packages/cli/package.json2
-rw-r--r--packages/server/package.json2
-rw-r--r--packages/server/src/config/config.ts13
-rw-r--r--packages/server/src/tools/shell.json18
-rw-r--r--packages/server/src/tools/shell.md1
-rw-r--r--packages/server/src/tools/shell.ts44
-rwxr-xr-xscripts/build_package.sh33
-rwxr-xr-xscripts/start_sandbox.sh7
8 files changed, 114 insertions, 6 deletions
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 3cbb97a1..c506bb65 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -8,7 +8,7 @@
"gemini-code": "dist/index.js"
},
"scripts": {
- "build": "tsc --build && touch dist/.last_build",
+ "build": "../../scripts/build_package.sh",
"clean": "rm -rf dist",
"start": "node dist/gemini.js",
"debug": "node --inspect-brk dist/gemini.js",
diff --git a/packages/server/package.json b/packages/server/package.json
index b1f862cf..1b0e31c8 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -6,7 +6,7 @@
"main": "dist/index.js",
"scripts": {
"start": "node dist/src/index.js",
- "build": "tsc --build",
+ "build": "../../scripts/build_package.sh",
"clean": "rm -rf dist",
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write .",
diff --git a/packages/server/src/config/config.ts b/packages/server/src/config/config.ts
index 06092b84..8a49380c 100644
--- a/packages/server/src/config/config.ts
+++ b/packages/server/src/config/config.ts
@@ -15,9 +15,11 @@ import { GrepTool } from '../tools/grep.js';
import { GlobTool } from '../tools/glob.js';
import { EditTool } from '../tools/edit.js';
import { TerminalTool } from '../tools/terminal.js';
+import { ShellTool } from '../tools/shell.js';
import { WriteFileTool } from '../tools/write-file.js';
import { WebFetchTool } from '../tools/web-fetch.js';
import { ReadManyFilesTool } from '../tools/read-many-files.js';
+import { BaseTool, ToolResult } from '../tools/tools.js';
const DEFAULT_PASSTHROUGH_COMMANDS = ['ls', 'git', 'npm'];
@@ -132,17 +134,24 @@ function createToolRegistry(config: Config): ToolRegistry {
const registry = new ToolRegistry();
const targetDir = config.getTargetDir();
- const tools = [
+ const tools: Array<BaseTool<unknown, ToolResult>> = [
new LSTool(targetDir),
new ReadFileTool(targetDir),
new GrepTool(targetDir),
new GlobTool(targetDir),
new EditTool(targetDir),
- new TerminalTool(targetDir, config),
new WriteFileTool(targetDir),
new WebFetchTool(), // Note: WebFetchTool takes no arguments
new ReadManyFilesTool(targetDir),
];
+
+ // use ShellTool (next-gen TerminalTool) if environment variable is set
+ if (process.env.SHELL_TOOL) {
+ tools.push(new ShellTool(targetDir, config));
+ } else {
+ tools.push(new TerminalTool(targetDir, config));
+ }
+
for (const tool of tools) {
registry.registerTool(tool);
}
diff --git a/packages/server/src/tools/shell.json b/packages/server/src/tools/shell.json
new file mode 100644
index 00000000..f1ba372e
--- /dev/null
+++ b/packages/server/src/tools/shell.json
@@ -0,0 +1,18 @@
+{
+ "type": "object",
+ "properties": {
+ "command": {
+ "description": "The exact bash command or sequence of commands (using ';' or '&&') to execute. Must adhere to usage guidelines. Example: 'npm install && npm run build'",
+ "type": "string"
+ },
+ "description": {
+ "description": "Optional: A brief, user-centric explanation of what the command does and why it's being run. Used for logging and confirmation prompts. Example: 'Install project dependencies'",
+ "type": "string"
+ },
+ "runInBackground": {
+ "description": "If true, execute the command in the background using '&'. Defaults to false. Use for servers or long tasks.",
+ "type": "boolean"
+ }
+ },
+ "required": ["command"]
+}
diff --git a/packages/server/src/tools/shell.md b/packages/server/src/tools/shell.md
new file mode 100644
index 00000000..ce71e42c
--- /dev/null
+++ b/packages/server/src/tools/shell.md
@@ -0,0 +1 @@
+This is a minimal shell tool.
diff --git a/packages/server/src/tools/shell.ts b/packages/server/src/tools/shell.ts
new file mode 100644
index 00000000..658293b0
--- /dev/null
+++ b/packages/server/src/tools/shell.ts
@@ -0,0 +1,44 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import path from 'path';
+import fs from 'fs';
+import { Config } from '../config/config.js';
+import { BaseTool, ToolResult } from './tools.js';
+
+export interface ShellToolParams {
+ command: string;
+ description?: string;
+}
+
+export class ShellTool extends BaseTool<ShellToolParams, ToolResult> {
+ static Name: string = 'execute_bash_command';
+ private readonly rootDirectory: string;
+ private readonly config: Config;
+
+ constructor(rootDirectory: string, config: Config) {
+ const toolDisplayName = 'Shell';
+ const descriptionUrl = new URL('shell.md', import.meta.url);
+ const toolDescription = fs.readFileSync(descriptionUrl, 'utf-8');
+ const schemaUrl = new URL('shell.json', import.meta.url);
+ const toolParameterSchema = JSON.parse(fs.readFileSync(schemaUrl, 'utf-8'));
+ super(
+ ShellTool.Name,
+ toolDisplayName,
+ toolDescription,
+ toolParameterSchema,
+ );
+ this.config = config;
+ this.rootDirectory = path.resolve(rootDirectory);
+ }
+
+ async execute(_params: ShellToolParams): Promise<ToolResult> {
+ return {
+ llmContent: 'hello',
+ returnDisplay: 'hello',
+ };
+ }
+}
diff --git a/scripts/build_package.sh b/scripts/build_package.sh
new file mode 100755
index 00000000..ba51b8a5
--- /dev/null
+++ b/scripts/build_package.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+if [[ $(pwd) != *"/packages/"* ]]; then
+ echo "must be invoked from a package directory"
+ exit 1
+fi
+
+# clean dist directory
+rm -rf dist/*
+
+# build typescript files
+tsc --build
+
+# copy .{md,json} files (replace -q with -i to see itemized changes)
+rsync -am -q --include='*.md' --include='*.json' --include='*/' --exclude='*' ./src/ ./dist/src/
+
+# touch dist/.last_build
+touch dist/.last_build
diff --git a/scripts/start_sandbox.sh b/scripts/start_sandbox.sh
index 2146a0c8..b17f1a7e 100755
--- a/scripts/start_sandbox.sh
+++ b/scripts/start_sandbox.sh
@@ -53,10 +53,13 @@ while [ "$current_dir" != "/" ]; do
current_dir=$(dirname "$current_dir")
done
-# if GEMINI_API_KEY is set, copy into container
+# copy GEMINI_API_KEY
if [ -n "${GEMINI_API_KEY:-}" ]; then run_args+=(--env GEMINI_API_KEY="$GEMINI_API_KEY"); fi
-# pass TERM and COLORTERM to container to maintain terminal colors
+# copy SHELL_TOOL to optionally enable shell tool
+if [ -n "${SHELL_TOOL:-}" ]; then run_args+=(--env SHELL_TOOL="$SHELL_TOOL"); fi
+
+# copy TERM and COLORTERM to try to maintain terminal setup
if [ -n "${TERM:-}" ]; then run_args+=(--env TERM="$TERM"); fi
if [ -n "${COLORTERM:-}" ]; then run_args+=(--env COLORTERM="$COLORTERM"); fi