ManimCat / src /studio-agent /tools /apply-patch-tool.ts
Bin29's picture
Sync from main: c1ef036 chore: document docker persistence volumes
94e1b2f
import type { StudioToolDefinition, StudioToolResult } from '../domain/types'
import type { StudioRuntimeBackedToolContext } from '../runtime/tool-runtime-context'
import { truncateToolText, toWorkspaceRelativePath } from './workspace-paths'
import { applyWorkspacePatch } from './workspace-edits'
interface ApplyPatchToolInput {
path?: string
file?: string
patches?: Array<{ search: string; replace: string; replaceAll?: boolean }>
}
export function createStudioApplyPatchTool(): StudioToolDefinition<ApplyPatchToolInput> {
return {
name: 'apply_patch',
description: 'Apply structured search/replace patches to a workspace file.',
category: 'edit',
permission: 'apply_patch',
allowedAgents: ['builder'],
requiresTask: false,
execute: async (input, context) => executeApplyPatchTool(input, context as StudioRuntimeBackedToolContext)
}
}
async function executeApplyPatchTool(
input: ApplyPatchToolInput,
context: StudioRuntimeBackedToolContext
): Promise<StudioToolResult> {
const target = input.path ?? input.file
if (!target || !Array.isArray(input.patches) || input.patches.length === 0) {
throw new Error('Apply_patch tool requires "path" and non-empty "patches"')
}
const result = await applyWorkspacePatch({
baseDirectory: context.session.directory,
targetPath: target,
patches: input.patches
})
const relativePath = toWorkspaceRelativePath(context.session.directory, result.absolutePath).replace(/\\/g, '/')
const output = truncateToolText(result.content)
return {
title: `Patched ${relativePath}`,
output: output.text,
metadata: {
path: relativePath,
absolutePath: result.absolutePath,
replacements: result.replacements,
patchCount: input.patches.length,
truncated: output.truncated
}
}
}