diff --git a/apps/x/packages/core/package.json b/apps/x/packages/core/package.json index fccc2f7a..5ea8038c 100644 --- a/apps/x/packages/core/package.json +++ b/apps/x/packages/core/package.json @@ -21,6 +21,7 @@ "ai": "^5.0.102", "awilix": "^12.0.5", "chokidar": "^4.0.3", + "glob": "^13.0.0", "google-auth-library": "^10.5.0", "googleapis": "^169.0.0", "node-html-markdown": "^2.0.0", diff --git a/apps/x/packages/core/src/application/assistant/instructions.ts b/apps/x/packages/core/src/application/assistant/instructions.ts index 629a7673..7a080785 100644 --- a/apps/x/packages/core/src/application/assistant/instructions.ts +++ b/apps/x/packages/core/src/application/assistant/instructions.ts @@ -94,7 +94,7 @@ When a user asks for ANY task that might require external capabilities (web sear **IMPORTANT**: Rowboat provides builtin tools that are internal and do NOT require security allowlist entries: - \`workspace-readFile\`, \`workspace-writeFile\`, \`workspace-edit\`, \`workspace-remove\` - File operations -- \`workspace-readdir\`, \`workspace-exists\`, \`workspace-stat\` - Directory exploration +- \`workspace-readdir\`, \`workspace-exists\`, \`workspace-stat\`, \`workspace-glob\` - Directory exploration and file search - \`workspace-mkdir\`, \`workspace-rename\`, \`workspace-copy\` - File/directory management - \`analyzeAgent\` - Agent analysis - \`addMcpServer\`, \`listMcpServers\`, \`listMcpTools\`, \`executeMcpTool\` - MCP server management and execution diff --git a/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts b/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts index 40a61960..6206d35b 100644 --- a/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts +++ b/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts @@ -176,6 +176,7 @@ The Rowboat copilot has access to special builtin tools that regular agents don' - \`workspace-rename\` - Rename or move files/directories - \`workspace-copy\` - Copy files - \`workspace-getRoot\` - Get workspace root directory path +- \`workspace-glob\` - Find files matching a glob pattern (e.g., "**/*.ts", "agents/*.md") #### Agent Operations - \`analyzeAgent\` - Read and analyze an agent file structure diff --git a/apps/x/packages/core/src/application/lib/builtin-tools.ts b/apps/x/packages/core/src/application/lib/builtin-tools.ts index dcd763de..8036c8de 100644 --- a/apps/x/packages/core/src/application/lib/builtin-tools.ts +++ b/apps/x/packages/core/src/application/lib/builtin-tools.ts @@ -1,5 +1,6 @@ import { z, ZodType } from "zod"; import * as path from "path"; +import { glob } from "glob"; import { executeCommand } from "./command-executor.js"; import { resolveSkill, availableSkills } from "../assistant/skills/index.js"; import { executeTool, listServers, listTools } from "../../mcp/mcp.js"; @@ -311,6 +312,40 @@ export const BuiltinTools: z.infer = { }, }, + 'workspace-glob': { + description: 'Find files matching a glob pattern (e.g., "**/*.ts", "src/**/*.json"). Much faster than recursive readdir for finding files.', + inputSchema: z.object({ + pattern: z.string().describe('Glob pattern to match files'), + cwd: z.string().optional().describe('Subdirectory to search in, relative to workspace root (default: workspace root)'), + }), + execute: async ({ pattern, cwd }: { pattern: string; cwd?: string }) => { + try { + const searchDir = cwd ? path.join(WorkDir, cwd) : WorkDir; + + // Ensure search directory is within workspace + const resolvedSearchDir = path.resolve(searchDir); + if (!resolvedSearchDir.startsWith(WorkDir)) { + return { error: 'Search directory must be within workspace' }; + } + + const files = await glob(pattern, { + cwd: searchDir, + nodir: true, + ignore: ['node_modules/**', '.git/**'], + }); + + return { + files, + count: files.length, + pattern, + cwd: cwd || '.', + }; + } catch (error) { + return { error: error instanceof Error ? error.message : 'Unknown error' }; + } + }, + }, + analyzeAgent: { description: 'Read and analyze an agent file to understand its structure, tools, and configuration', inputSchema: z.object({ diff --git a/apps/x/pnpm-lock.yaml b/apps/x/pnpm-lock.yaml index e4d1ef11..60c7a655 100644 --- a/apps/x/pnpm-lock.yaml +++ b/apps/x/pnpm-lock.yaml @@ -278,6 +278,9 @@ importers: chokidar: specifier: ^4.0.3 version: 4.0.3 + glob: + specifier: ^13.0.0 + version: 13.0.0 google-auth-library: specifier: ^10.5.0 version: 10.5.0 @@ -871,6 +874,14 @@ packages: '@iconify/utils@3.1.0': resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2975,6 +2986,10 @@ packages: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} + engines: {node: 20 || >=22} + global-agent@3.0.0: resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} engines: {node: '>=10.0'} @@ -3419,6 +3434,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3668,6 +3687,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3861,6 +3884,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -5099,6 +5126,12 @@ snapshots: '@iconify/types': 2.0.0 mlly: 1.8.0 + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -7402,6 +7435,12 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@13.0.0: + dependencies: + minimatch: 10.1.1 + minipass: 7.1.2 + path-scurry: 2.0.1 + global-agent@3.0.0: dependencies: boolean: 3.2.0 @@ -7909,6 +7948,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.4: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -8397,6 +8438,10 @@ snapshots: mimic-response@3.1.0: {} + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -8576,6 +8621,11 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.4 + minipass: 7.1.2 + path-to-regexp@8.3.0: {} pathe@2.0.3: {}