diff --git a/.gitignore b/.gitignore index 6c4ee52..ce230a9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ venv/ *.log logs/ .tmp/ - +test/ # IDE .vscode/ .idea/ diff --git a/AGENTS.md b/AGENTS.md index ecf646d..a360984 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,8 +38,10 @@ - `Read`:用于读取文件内容。 - `Glob`:用于按模式查找文件。 +- 创建或覆盖文件时,优先使用 `Write` 工具。 - `Bash`:用于执行必须通过 shell 完成的最小命令。 - 只有在 Bash 确实必要时才使用 Bash。 +- 不要用 Bash 拼接文件内容。 - Windows 环境下优先使用兼容写法,不默认使用 `cat < list[Tool]: }, execute=lambda data: glob_tool(workspace, data), ), + Tool( + name="Write", + description="创建或覆盖工作区内的文本文件。", + input_schema={ + "type": "object", + "properties": { + "path": {"type": "string", "description": "相对工作区的文件路径"}, + "content": {"type": "string", "description": "要写入的完整文本内容"}, + "encoding": {"type": "string", "description": "文件编码,默认 utf-8"}, + }, + "required": ["path", "content"], + "additionalProperties": False, + }, + execute=lambda data: write_tool(workspace, data), + ), Tool( name="Bash", description="在工作区中执行一条 shell 命令。", @@ -82,6 +97,20 @@ def glob_tool(workspace: Path, data: dict[str, Any]) -> str: return "\n".join(matches[:200]) +def write_tool(workspace: Path, data: dict[str, Any]) -> str: + path = _safe_path(workspace, str(data["path"])) + if path.exists() and path.is_dir(): + return f"目标是目录,不能写入文件: {path.relative_to(workspace)}" + if not path.parent.exists(): + return f"父目录不存在: {path.parent.relative_to(workspace)}" + + existed = path.exists() + encoding = str(data.get("encoding") or "utf-8") + path.write_text(str(data["content"]), encoding=encoding) + action = "已覆盖" if existed else "已创建" + return f"{action}文件: {path.relative_to(workspace)}" + + def bash_tool(workspace: Path, data: dict[str, Any]) -> str: command = str(data["command"]) proc = subprocess.run(