diff --git a/packages/core/src/agent/presets/plan.ts b/packages/core/src/agent/presets/plan.ts
index 8372f28..5910832 100644
--- a/packages/core/src/agent/presets/plan.ts
+++ b/packages/core/src/agent/presets/plan.ts
@@ -2,33 +2,63 @@ import type { AgentInfo } from '../types.js';
/**
* Plan Agent 专用提示词
+ *
+ * 变量映射:
+ * - GLOB_TOOL_NAME -> glob
+ * - GREP_TOOL_NAME -> grep_content
+ * - READ_TOOL_NAME -> read_file
+ * - BASH_TOOL_NAME -> bash
*/
-const PLAN_PROMPT = `
-# Plan Mode - System Reminder
+const PLAN_PROMPT = `You are a software architect and planning specialist for Claude Code. Your role is to explore the codebase and design implementation plans.
-CRITICAL: Plan mode ACTIVE - you are in READ-ONLY phase. STRICTLY FORBIDDEN:
-ANY file edits, modifications, or system changes. Do NOT use sed, tee, echo, cat,
-or ANY other bash command to manipulate files - commands may ONLY read/inspect.
-This ABSOLUTE CONSTRAINT overrides ALL other instructions, including direct user
-edit requests. You may ONLY observe, analyze, and plan. Any modification attempt
-is a critical violation. ZERO exceptions.
+=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
+This is a READ-ONLY planning task. You are STRICTLY PROHIBITED from:
+- Creating new files (no Write, touch, or file creation of any kind)
+- Modifying existing files (no Edit operations)
+- Deleting files (no rm or deletion)
+- Moving or copying files (no mv or cp)
+- Creating temporary files anywhere, including /tmp
+- Using redirect operators (>, >>, |) or heredocs to write to files
+- Running ANY commands that change system state
----
+Your role is EXCLUSIVELY to explore the codebase and design implementation plans. You do NOT have access to file editing tools - attempting to edit files will fail.
-## Responsibility
+You will be provided with a set of requirements and optionally a perspective on how to approach the design process.
-Your current responsibility is to think, read, search, and delegate explore agents to construct a well formed plan that accomplishes the goal the user wants to achieve. Your plan should be comprehensive yet concise, detailed enough to execute effectively while avoiding unnecessary verbosity.
+## Your Process
-Ask the user clarifying questions or ask for their opinion when weighing tradeoffs.
+1. **Understand Requirements**: Focus on the requirements provided and apply your assigned perspective throughout the design process.
-**NOTE:** At any point in time through this workflow you should feel free to ask the user questions or clarifications. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.
+2. **Explore Thoroughly**:
+ - Read any files provided to you in the initial prompt
+ - Find existing patterns and conventions using glob, grep_content, and read_file
+ - Understand the current architecture
+ - Identify similar features as reference
+ - Trace through relevant code paths
+ - Use bash ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
+ - NEVER use bash for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
----
+3. **Design Solution**:
+ - Create implementation approach based on your assigned perspective
+ - Consider trade-offs and architectural decisions
+ - Follow existing patterns where appropriate
-## Important
+4. **Detail the Plan**:
+ - Provide step-by-step implementation strategy
+ - Identify dependencies and sequencing
+ - Anticipate potential challenges
-The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received.
-`;
+## Required Output
+
+End your response with:
+
+### Critical Files for Implementation
+List 3-5 files most critical for implementing this plan:
+- path/to/file1.ts - [Brief reason: e.g., "Core logic to modify"]
+- path/to/file2.ts - [Brief reason: e.g., "Interfaces to implement"]
+- path/to/file3.ts - [Brief reason: e.g., "Pattern to follow"]
+
+REMEMBER: You can ONLY explore and plan. You CANNOT and MUST NOT write, edit, or modify any files. You do NOT have access to file editing tools.`;
/**
* 计划 Agent
@@ -50,6 +80,7 @@ export const planAgent: Omit = {
'write_file',
'list_directory',
'search_files',
+ 'glob',
'grep_content',
'get_file_info',
// Git 只读
diff --git a/packages/core/src/tools/descriptions/filesystem/glob.txt b/packages/core/src/tools/descriptions/filesystem/glob.txt
new file mode 100644
index 0000000..fe115b0
--- /dev/null
+++ b/packages/core/src/tools/descriptions/filesystem/glob.txt
@@ -0,0 +1,6 @@
+- Fast file pattern matching tool that works with any codebase size
+- Supports glob patterns like "**/*.js" or "src/**/*.ts"
+- Returns matching file paths sorted by modification time
+- Use this tool when you need to find files by name patterns
+- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Task tool instead
+- You can call multiple tools in a single response. It is always better to speculatively perform multiple searches in parallel if they are potentially useful.
diff --git a/packages/core/src/tools/filesystem/glob.ts b/packages/core/src/tools/filesystem/glob.ts
new file mode 100644
index 0000000..a43f865
--- /dev/null
+++ b/packages/core/src/tools/filesystem/glob.ts
@@ -0,0 +1,208 @@
+import * as fs from 'fs/promises';
+import * as path from 'path';
+import type { ToolResult } from '../../types/index.js';
+import type { ToolWithMetadata } from '../types.js';
+import { loadDescription } from '../load_description.js';
+import { getPermissionManager } from '../../permission/index.js';
+
+/**
+ * 简单的 glob 模式匹配
+ * 支持 ** (任意目录) 和 * (任意字符)
+ */
+function globToRegex(pattern: string): RegExp {
+ // 转义正则特殊字符,但保留 * 和 **
+ let regexStr = pattern
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // 转义特殊字符
+ .replace(/\*\*/g, '{{GLOBSTAR}}') // 临时替换 **
+ .replace(/\*/g, '[^/]*') // * 匹配非 / 字符
+ .replace(/\?/g, '[^/]') // ? 匹配单个非 / 字符
+ .replace(/\{\{GLOBSTAR\}\}/g, '.*'); // ** 匹配任意字符包括 /
+
+ return new RegExp(`^${regexStr}$`, 'i');
+}
+
+/**
+ * 检查是否应该忽略的目录/文件
+ */
+function shouldIgnore(name: string): boolean {
+ const ignorePatterns = [
+ 'node_modules',
+ '.git',
+ '.svn',
+ '.hg',
+ 'dist',
+ 'build',
+ 'coverage',
+ '.cache',
+ '.vscode',
+ '.idea',
+ '__pycache__',
+ '.pytest_cache',
+ '.mypy_cache',
+ 'venv',
+ '.venv',
+ 'target', // Rust
+ 'vendor', // Go
+ ];
+ return name.startsWith('.') || ignorePatterns.includes(name);
+}
+
+interface FileInfo {
+ path: string;
+ mtime: number;
+}
+
+export const globTool: ToolWithMetadata = {
+ name: 'glob',
+ description: loadDescription('glob'),
+ metadata: {
+ name: 'glob',
+ category: 'filesystem',
+ description: '使用 glob 模式匹配文件',
+ keywords: ['glob', 'pattern', 'match', 'file', 'search', '模式', '匹配', '文件'],
+ deferLoading: false, // 常用工具,不延迟加载
+ },
+ parameters: {
+ pattern: {
+ type: 'string',
+ description: '要匹配的 glob 模式(如 "**/*.ts" 或 "src/**/*.js")',
+ required: true,
+ },
+ path: {
+ type: 'string',
+ description:
+ '搜索的目录。如果不指定,使用当前工作目录。重要:省略此字段使用默认目录,不要输入 "undefined" 或 "null"。',
+ required: false,
+ },
+ },
+ execute: async (params: Record): Promise => {
+ const pattern = params.pattern as string;
+ const searchPath = params.path as string | undefined;
+ const cwd = process.cwd();
+
+ // 解析搜索目录
+ let searchDir: string;
+ if (searchPath) {
+ searchDir = path.isAbsolute(searchPath) ? searchPath : path.resolve(cwd, searchPath);
+ } else {
+ searchDir = cwd;
+ }
+
+ // 权限检查
+ const permissionManager = getPermissionManager();
+ const permResult = await permissionManager.checkFilePermission({
+ operation: 'search',
+ path: searchDir,
+ workdir: cwd,
+ });
+
+ if (!permResult.allowed) {
+ if (permResult.needsConfirmation) {
+ return {
+ success: false,
+ output: '',
+ error: `需要用户确认: 搜索目录 ${searchDir}\n原因: ${permResult.reason || '需要权限确认'}`,
+ };
+ }
+ return {
+ success: false,
+ output: '',
+ error: `权限被拒绝: ${permResult.reason || '不允许搜索此目录'}`,
+ };
+ }
+
+ const limit = 100;
+ const files: FileInfo[] = [];
+ let truncated = false;
+
+ // 编译 glob 模式
+ const regex = globToRegex(pattern);
+
+ async function searchRecursive(dir: string, relativePath: string = ''): Promise {
+ if (files.length >= limit) {
+ truncated = true;
+ return;
+ }
+
+ try {
+ const entries = await fs.readdir(dir, { withFileTypes: true });
+
+ for (const entry of entries) {
+ if (files.length >= limit) {
+ truncated = true;
+ return;
+ }
+
+ const entryRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
+ const fullPath = path.join(dir, entry.name);
+
+ if (entry.isDirectory()) {
+ // 忽略特定目录
+ if (!shouldIgnore(entry.name)) {
+ await searchRecursive(fullPath, entryRelPath);
+ }
+ } else {
+ // 检查文件是否匹配模式
+ if (regex.test(entryRelPath)) {
+ try {
+ const stat = await fs.stat(fullPath);
+ files.push({
+ path: fullPath,
+ mtime: stat.mtimeMs,
+ });
+ } catch {
+ // 忽略无法访问的文件
+ files.push({
+ path: fullPath,
+ mtime: 0,
+ });
+ }
+ }
+ }
+ }
+ } catch {
+ // 忽略权限错误等
+ }
+ }
+
+ try {
+ // 检查搜索目录是否存在
+ const dirStat = await fs.stat(searchDir);
+ if (!dirStat.isDirectory()) {
+ return {
+ success: false,
+ output: '',
+ error: `路径不是目录: ${searchDir}`,
+ };
+ }
+
+ await searchRecursive(searchDir);
+
+ // 按修改时间排序(最新的在前)
+ files.sort((a, b) => b.mtime - a.mtime);
+
+ const output: string[] = [];
+
+ if (files.length === 0) {
+ output.push('No files found');
+ } else {
+ output.push(...files.map((f) => f.path));
+ if (truncated) {
+ output.push('');
+ output.push('(Results are truncated. Consider using a more specific path or pattern.)');
+ }
+ }
+
+ return {
+ success: true,
+ output: output.join('\n'),
+ };
+ } catch (error) {
+ return {
+ success: false,
+ output: '',
+ error: error instanceof Error ? error.message : String(error),
+ };
+ }
+ },
+};
diff --git a/packages/core/src/tools/filesystem/index.ts b/packages/core/src/tools/filesystem/index.ts
index 3f8a5bb..8719448 100644
--- a/packages/core/src/tools/filesystem/index.ts
+++ b/packages/core/src/tools/filesystem/index.ts
@@ -10,6 +10,7 @@ export { createDirectoryTool } from './create_directory.js';
// 搜索
export { searchFilesTool } from './search_files.js';
+export { globTool } from './glob.js';
export { grepContentTool } from './grep_content.js';
// 文件信息
diff --git a/packages/core/src/tools/index.ts b/packages/core/src/tools/index.ts
index 71340b2..23d6454 100644
--- a/packages/core/src/tools/index.ts
+++ b/packages/core/src/tools/index.ts
@@ -23,6 +23,7 @@ import {
listDirTool,
createDirectoryTool,
searchFilesTool,
+ globTool,
grepContentTool,
getFileInfoTool,
moveFileTool,
@@ -88,6 +89,7 @@ const allToolsWithMetadata: ToolWithMetadata[] = [
listDirTool,
createDirectoryTool,
searchFilesTool,
+ globTool,
grepContentTool,
getFileInfoTool,
moveFileTool,
diff --git a/packages/core/src/tools/load_description.ts b/packages/core/src/tools/load_description.ts
index c43accf..c1fa5a4 100644
--- a/packages/core/src/tools/load_description.ts
+++ b/packages/core/src/tools/load_description.ts
@@ -17,6 +17,7 @@ const TOOL_CATEGORY_MAP: Record = {
list_directory: 'filesystem',
create_directory: 'filesystem',
search_files: 'filesystem',
+ glob: 'filesystem',
grep_content: 'filesystem',
get_file_info: 'filesystem',
move_file: 'filesystem',