feat: 升级至 v0.3.12 — 文件与代码类工具全面优化(16 项)

【阶段一 · 紧急修复 F1】
- F1-1 file_editor regex 计数 bug:强制 g 标志 + 同一 RegExp 实例做 match+replace
- F1-2 code_search 统一 safeResolvePath(含路径遍历 + MEMORY.md 拦截)
- F1-3 git_commit amend 模式:message 可选 + --no-edit 防止编辑器 hang
- F1-4 write_file append 模式原子化:显式 open(O_APPEND)+write+fsync+close

【阶段二 · 增强现有工具 F2】
- F2-1 read_file 智能编码检测:BOM(UTF-8/UTF-16 LE/BE) + GBK 降级
- F2-2 read_file 支持 tail 模式(读取末尾 N 行,适用日志)
- F2-3 search_files 二进制过滤 + 智能编码检测
- F2-4 search_files 多 glob 匹配(逗号分隔,如 *.ts,*.js)
- F2-5 file_editor 新增 find_replace 操作(字面量替换,规避正则歧义)
- F2-6 file_editor regex 支持跨行匹配(multiline 参数)
- F2-7 file_editor 新增 backup 参数(编辑前 .bak 备份)

【阶段三 · 新增工具 F3】
- F3-1 file_move:双路径校验 + overwrite + 自动建父目录 + rename 原子
- F3-2 file_info:大小/时间/类型/编码/二进制/权限位

【阶段四 · 优化 F4】
- F4-1 diff_viewer Uint32Array→Uint16Array(省一半内存)+ safeResolvePath + 智能编码
- F4-2 错误处理统一:diff-viewer/file-editor/code-search catch 块改用 extractErrorMessage

【阶段五 · 验证 F5】
- tsc --noEmit 类型检查通过
- 人工审查通过:路径校验/错误处理/原子性/资源释放/权限策略/导出注册

工具数量:13 → 15(新增 file_move / file_info)
This commit is contained in:
2026-07-21 16:08:39 +08:00
parent 8973ea6d47
commit 058ee2de36
12 changed files with 586 additions and 100 deletions
+23 -8
View File
@@ -340,11 +340,11 @@ export class GitLogTool implements IMetonaTool {
export class GitCommitTool implements IMetonaTool {
readonly definition: MetonaToolDef = {
name: 'git_commit',
description: 'Stage files and create a git commit. Supports amending the previous commit. Requires user confirmation.',
description: 'Stage files and create a git commit. Supports amending the previous commit. In amend mode, message is optional (omitting it keeps the original commit message). Requires user confirmation.',
parameters: {
type: 'object',
properties: {
message: { type: 'string', description: 'Commit message' },
message: { type: 'string', description: 'Commit message. Required for new commits. Optional in amend mode (omitting keeps original message).' },
files: {
type: 'array',
items: { type: 'string', description: 'File path to stage' },
@@ -352,7 +352,8 @@ export class GitCommitTool implements IMetonaTool {
},
amend: { type: 'boolean', description: 'Amend the previous commit instead of creating a new one (default false)' },
},
required: ['message'],
// F1-3: message 不再硬性 required — amend 模式下可省略(保留原 message)
// 校验在 execute 内根据 amend 标志动态进行
},
category: MetonaToolCategory.FILESYSTEM,
riskLevel: MetonaRiskLevel.MEDIUM,
@@ -361,12 +362,17 @@ export class GitCommitTool implements IMetonaTool {
};
async execute(args: Record<string, unknown>, context: ToolExecutionContext): Promise<unknown> {
const message = args.message as string;
const message = args.message as string | undefined;
const files = (args.files as string[] | undefined) ?? [];
const amend = (args.amend as boolean) ?? false;
if (!message || message.trim().length === 0) {
return { success: false, error: 'Commit message is required' };
// F1-3: 非 amend 模式必须有 messageamend 模式可省略(保留原 message)
if (!amend && (!message || message.trim().length === 0)) {
return { success: false, error: 'Commit message is required (use amend: true to reuse the previous message)' };
}
// amend 模式下若提供 message,需校验非空字符串
if (amend && message !== undefined && message.trim().length === 0) {
return { success: false, error: 'Commit message must not be empty (or omit message field to keep original)' };
}
try {
@@ -393,9 +399,16 @@ export class GitCommitTool implements IMetonaTool {
const filesChanged = stagedOutput.split('\n').filter((l) => l.length > 0).length;
// 3. 提交(commit 可能触发 hooks,给予更长超时)
// F1-3: amend 模式下若未提供 message,不加 -m 参数(保留原 message
const commitArgs = ['commit'];
if (amend) commitArgs.push('--amend');
commitArgs.push('-m', message);
if (message && message.trim().length > 0) {
commitArgs.push('-m', message);
} else if (amend) {
// amend + 无 message + 无 --no-edit → git 会打开编辑器要求输入
// 加 --no-edit 明确表示保留原 message(防止 hang 等待编辑器)
commitArgs.push('--no-edit');
}
await runGit(commitArgs, context.workspacePath, 20_000);
// 4. 获取提交 hash 和当前分支
@@ -408,8 +421,10 @@ export class GitCommitTool implements IMetonaTool {
success: true,
commit,
branch,
message,
// F1-3: amend 模式下若未提供 message,返回值为空字符串(实际 message 保留原值)
message: message ?? (amend ? '(amended - original message preserved)' : ''),
filesChanged,
amended: amend,
};
} catch (error) {
if (isNotGitRepoError(error)) {