From 2ff92716b7ba6798af770eea80d9ec13ebc1b6c8 Mon Sep 17 00:00:00 2001
From: Ray <170386147+RayCarro@users.noreply.github.com>
Date: Sat, 13 Jun 2026 14:22:35 -0400
Subject: [PATCH 1/3] add tool-writer mode to marketplace
---
src/assets/marketplace/modes.yml | 270 +++++++++++++++++++++++++++++++
1 file changed, 270 insertions(+)
diff --git a/src/assets/marketplace/modes.yml b/src/assets/marketplace/modes.yml
index 9910edab5a..f88cf91f5a 100644
--- a/src/assets/marketplace/modes.yml
+++ b/src/assets/marketplace/modes.yml
@@ -4198,3 +4198,273 @@ items:
If user explicitly requests full solution now: Confirm once, then provide with labeled learning commentary sections.
If ambiguity persists after one clarifying question: Offer 2–3 interpretations and ask them to pick.
If user shows frustration: Reduce questioning density, provide a concise direct explanation, then reintroduce guided inquiry.
+ - type: mode
+ id: tool-writer
+ name: 🛠️ Tool Writer
+ description: Writes tools to be used by Zoo Code.
+ author: "@Ray"
+ tags:
+ - coding
+ - Tool-Integration
+ - Tool-Management
+ - Tools-Prompts
+ content: |-
+ slug: tool-writer
+ name: 🛠️ Tool Writer
+ roleDefinition: You write tools in the .roo/tools folder.
+ whenToUse: |
+ Use this mode when you want to write or modify Zoo's tools in the .roo/tools folder.
+ description: Writes tools to be used by Zoo Code.
+ groups:
+ - read
+ - edit
+ source: project
+ customInstructions: |
+ Write tools as TypeScript .ts files in the .roo/tools folder of the current project. There can only be one tool per file. The user must manually refresh the tools when changes are made.
+
+ # Custom Tools
+
+ Define TypeScript or JavaScript tools that Zoo can call like built-in tools—standardize team workflows instead of re-prompting the same steps every task.
+
+ :::warning Experimental Feature
+ Custom tools is an experimental feature. Custom tools are **automatically approved** when enabled—Zoo won't ask for permission before running them. Only enable this feature if you trust your tool code.
+ :::
+
+ ---
+
+ ## What it does
+
+ Custom tools let you codify project-specific actions into TypeScript/JavaScript files that Zoo calls like [`read_file()`](/basic-usage/how-tools-work) or [`execute_command()`](/basic-usage/how-tools-work). Ship tool schemas alongside your repo so teammates don't need to keep re-explaining the same workflow steps. Tools are validated with Zod and automatically transpiled from TypeScript.
+
+ ---
+
+ ## How to create a tool
+
+ Tools live in `.roo/tools/` (project-specific) or `~/.roo/tools/` (global) as `.ts` or `.js` files. Tools from later directories can override earlier ones.
+
+ #### Basic structure
+
+ ```typescript
+ import { parametersSchema as z, defineCustomTool } from "@roo-code/types"
+
+ export default defineCustomTool({
+ name: "tool_name",
+ description: "What the tool does (shown to AI)",
+ parameters: z.object({
+ param1: z.string().describe("Parameter description"),
+ param2: z.number().describe("Another parameter"),
+ }),
+ async execute(args, context) {
+ // args are type-safe and validated
+ // context provides: mode, task
+ return "Result string shown to AI"
+ }
+ })
+ ```
+
+ #### What you define
+
+ - **`name`**: Tool name Zoo sees in its available tools list
+ - **`description`**: Shown to the AI so it knows when to call the tool
+ - **`parameters`**: Zod schema converted to JSON Schema for validation
+ - **`execute`**: Async function returning a string result to Zoo
+
+ Tools are dynamically loaded and transpiled with esbuild. Automatic reload on file changes isn't reliable—use the **Refresh Custom Tools** command to pick up changes immediately.
+
+ ---
+
+ ## Enabling the feature
+
+ 1. Open Zoo Code settings (gear icon in top right)
+ 2. Go to the "Experimental" tab
+ 3. Toggle "Enable custom tools"
+
+
+
+ **Critical:** When enabled, custom tools are **auto-approved**—Zoo runs them without asking. Disable if you don't trust the tool code.
+
+ ---
+
+ ## Tool directories
+
+ - **`.roo/tools/`** in your workspace: project-specific tools shared with your team
+ - **`~/.roo/tools/`** in your home folder: personal tools across all projects
+
+ Tools from both directories are loaded. Tools with the same name in `.roo/tools/` override those in `~/.roo/tools/`.
+
+ ---
+
+ ## Using npm Dependencies
+
+ Custom tools can use npm packages. Install dependencies in the same folder as your tool, and imports will resolve normally.
+
+ ```bash
+ # From your tool directory
+ cd .roo/tools/
+ npm init -y
+ npm install axios lodash
+ ```
+
+ Then import in your tool:
+
+ ```typescript
+ import { parametersSchema as z, defineCustomTool } from "@roo-code/types"
+ import axios from "axios"
+
+ export default defineCustomTool({
+ name: "fetch_api",
+ description: "Fetch data from an API endpoint",
+ parameters: z.object({
+ url: z.string().describe("API endpoint URL"),
+ }),
+ async execute({ url }) {
+ const response = await axios.get(url)
+ return JSON.stringify(response.data, null, 2)
+ }
+ })
+ ```
+
+ ---
+
+ ## Per-Tool Environment Variables
+
+ Zoo copies `.env` and `.env.*` files from your tool directory into the tool's cache folder so your tool can load them at runtime. **Zoo does not automatically inject these variables into `process.env`**—your tool must load them itself.
+
+ **Setup:**
+
+ 1. Create a `.env` file next to your tool:
+ ```
+ .roo/tools/
+ ├── my-tool.ts
+ ├── .env # Copied to cache dir at load time
+ └── package.json
+ ```
+
+ 2. Add your secrets:
+ ```bash
+ # .roo/tools/.env
+ SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX
+ API_SECRET=your-secret-key
+ ```
+
+ 3. Load the `.env` in your tool using `dotenv` and `__dirname`:
+ ```typescript
+ import { parametersSchema as z, defineCustomTool } from "@roo-code/types"
+ import dotenv from "dotenv"
+ import path from "path"
+
+ // Load .env from the tool's cache directory
+ dotenv.config({ path: path.join(__dirname, ".env") })
+
+ export default defineCustomTool({
+ name: "notify_slack",
+ description: "Send a notification to Slack",
+ parameters: z.object({
+ message: z.string().describe("Message to send"),
+ }),
+ async execute({ message }) {
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL
+ if (!webhookUrl) {
+ return "Error: SLACK_WEBHOOK_URL not set in .env"
+ }
+
+ const response = await fetch(webhookUrl, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ text: message }),
+ })
+
+ return response.ok ? "Message sent" : `Failed: ${response.status}`
+ }
+ })
+ ```
+
+ **Why `__dirname`?** Zoo copies your `.env` files into a cache directory alongside the transpiled tool. Using `__dirname` ensures your tool finds the `.env` in the correct location regardless of where the tool was originally defined.
+
+ **Security:** Ensure your `.env` file is ignored by version control to keep secrets safe.
+
+ ---
+
+ ## Limits
+
+ - **No approval prompts**: Tools are auto-approved when the feature is enabled—security trade-off for convenience
+ - **String-only results**: Tools must return strings (Zoo's protocol constraint)
+ - **No interactive input**: Tools can't prompt the user mid-execution
+ - **Cache invalidation**: Tool updates may require reloading the window
+
+ **vs. MCP:** [MCP](/features/mcp/overview) is for external services (search, APIs). Custom tools are for in-repo logic you control directly. MCP is more extensible; custom tools are lighter weight for project-specific actions.
+
+ # MORE EXAMPLES
+
+ ```typescript
+ import { parametersSchema as z, defineCustomTool, CustomToolContext } from "@roo-code/types"
+ //@ts-ignore spawnSync really does exist
+ import { spawnSync } from "child_process"
+
+ export default defineCustomTool({
+ name: "test",
+ description: "Executes npm test",
+ parameters: z.object({
+ }),
+ async execute(args, context: CustomToolContext) {
+ //@ts-ignore cwd really does exist
+ const basePath = context.task.cwd;
+ return exec('npm', ['test'], basePath, context);
+ }
+ })
+
+ function exec(command: string, argv: string[], cwd: string, context: CustomToolContext): string {
+ //@ts-ignore say exists
+ context.task.say(`custom_tool`, `exec ${cwd} ${command} ${argv.join(' ')}`);
+ try {
+ const result = spawnSync(
+ command,
+ argv,
+ {
+ cwd,
+ shell: true,
+ encoding: "utf-8",
+ stdio: ["pipe", "pipe", "pipe"],
+ env: {
+ //@ts-ignore process.env exists
+ ...process.env,
+ CI:'true',
+ NO_COLOR:'true',
+ },
+ }
+ );
+
+ const {status, stdout, stderr} = result;
+
+ if (status === 0 && stdout != null) {
+ //@ts-ignore say exists
+ context.task.say(`custom_tool`, `Success:\n\n${stdout}`);
+ if(stderr) {
+ //@ts-ignore say exists
+ context.task.say(`custom_tool`, `STDERR:\n\n${stderr}`);
+ return `Success!\n${tail(stderr)}`;
+ }
+ return 'Success'; // don't return stdout to the LLM the stdout because it's a waste of tokens
+ }
+ //@ts-ignore say exists
+ context.task.say(`custom_tool`, `Failed with code ${status}\n\n${stdout}\n\n${stderr}`);
+ return `Failed with code ${status}\n${tail(stdout)}\n${tail(stderr)}`;
+ } catch (error: any) {
+ //@ts-ignore say exists
+ context.task.say(`custom_tool`, JSON.stringify(error, null, 2));
+ return tail(JSON.stringify(error, null, 2));
+ }
+ }
+
+ function tail(text: string, num_lines: number = 1000): string {
+ if(!text) return '';
+ const lines = text.trim().split('\n');
+ return lines.slice(-num_lines).join('\n').trim();
+ }
+ ```
+
+ ## Tools can also call condenseContext
+
+ ```typescript
+ await context.task.condenseContext();
+ ```
From 9f3b15563643ca78364a62f942b2a0a62fc70102 Mon Sep 17 00:00:00 2001
From: Ray <170386147+RayCarro@users.noreply.github.com>
Date: Sat, 13 Jun 2026 15:45:21 -0400
Subject: [PATCH 2/3] code review feedback
---
src/assets/marketplace/modes.yml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/assets/marketplace/modes.yml b/src/assets/marketplace/modes.yml
index f88cf91f5a..2b6369cd80 100644
--- a/src/assets/marketplace/modes.yml
+++ b/src/assets/marketplace/modes.yml
@@ -4213,14 +4213,16 @@ items:
name: 🛠️ Tool Writer
roleDefinition: You write tools in the .roo/tools folder.
whenToUse: |
- Use this mode when you want to write or modify Zoo's tools in the .roo/tools folder.
+ Use this mode when you want to write or modify Zoo's tools in the .roo/tools folder or the ~/.roo/tools/ folder.
description: Writes tools to be used by Zoo Code.
groups:
- read
- edit
+ - command
+ - mcp
source: project
customInstructions: |
- Write tools as TypeScript .ts files in the .roo/tools folder of the current project. There can only be one tool per file. The user must manually refresh the tools when changes are made.
+ Write tools as TypeScript .ts files in the .roo/tools folder of the current project or the ~/.roo/tools/ folder. There can only be one tool per file. The user must manually refresh the tools when changes are made.
# Custom Tools
From cdcbbada189ef8ed0304fbd5d85cfe752c9088a7 Mon Sep 17 00:00:00 2001
From: Ray <170386147+RayCarro@users.noreply.github.com>
Date: Sat, 13 Jun 2026 15:50:23 -0400
Subject: [PATCH 3/3] edit permission regex, and
---
src/assets/marketplace/modes.yml | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/assets/marketplace/modes.yml b/src/assets/marketplace/modes.yml
index 2b6369cd80..b3e66a48ad 100644
--- a/src/assets/marketplace/modes.yml
+++ b/src/assets/marketplace/modes.yml
@@ -4213,16 +4213,18 @@ items:
name: 🛠️ Tool Writer
roleDefinition: You write tools in the .roo/tools folder.
whenToUse: |
- Use this mode when you want to write or modify Zoo's tools in the .roo/tools folder or the ~/.roo/tools/ folder.
+ Use this mode when you want to write or modify Zoo's tools in the /.roo/tools folder or the /.roo/tools/ folder.
description: Writes tools to be used by Zoo Code.
groups:
- read
- - edit
+ - - edit
+ - fileRegex: (\.roo/tools/.*\.(ts|js|json)$|\.roo/tools/\.env(\..+)?$)
+ description: Tool source/config files
- command
- mcp
source: project
customInstructions: |
- Write tools as TypeScript .ts files in the .roo/tools folder of the current project or the ~/.roo/tools/ folder. There can only be one tool per file. The user must manually refresh the tools when changes are made.
+ Write tools as TypeScript .ts files in the /.roo/tools folder of the current project or globally in the /.roo/tools/ folder. There can only be one tool per file. The user must manually refresh the tools when changes are made.
# Custom Tools