Skip to main content
Glama

overwrite

Replace selected text lines with new content in text files. The tool updates files by substituting original lines with specified replacements, handling different line counts.

Instructions

Overwrite the selected lines with new text. Amount of new lines can differ from the original selection

Args: new_lines (dict): Example: {"lines":["line one", "second line"]}

Returns: dict: Diff preview showing the proposed changes, and any syntax errors for JS or Python

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
new_linesYes

Implementation Reference

  • The primary handler function implementing the 'overwrite' tool logic. Verifies selection integrity via ID check, processes new content lines (adding newlines as needed), generates a diff preview with context, performs syntax validation for Python (using Black) and JavaScript/JSX (using Babel), stores pending changes and diff for confirmation, and returns preview or syntax error details.
    async def overwrite(
        new_lines: dict,
    ) -> Dict[str, Any]:
        """
        Overwrite the selected lines with new text. Amount of new lines can differ from the original selection
    
        Args:
            new_lines (dict): Example: {"lines":["line one", "second line"]}
    
        Returns:
            dict: Diff preview showing the proposed changes, and any syntax errors for JS or Python
    
        """
        new_lines = new_lines.get("lines")
        if self.current_file_path is None:
            return {"error": "No file path is set. Use set_file first."}
    
        if (
            self.selected_start is None
            or self.selected_end is None
            or self.selected_id is None
        ):
            return {"error": "No selection has been made. Use select tool first."}
    
        try:
            with open(self.current_file_path, "r", encoding="utf-8") as file:
                lines = file.readlines()
        except Exception as e:
            return {"error": f"Error reading file: {str(e)}"}
    
        start = self.selected_start
        end = self.selected_end
        id = self.selected_id
    
        current_content = "".join(lines[start - 1 : end])
    
        computed_id = calculate_id(current_content, start, end)
    
        if computed_id != id:
            return {
                "error": "id verification failed. The content may have been modified since you last read it."
            }
    
        processed_new_lines = []
        for line in new_lines:
            if not line.endswith("\n"):
                processed_new_lines.append(line + "\n")
            else:
                processed_new_lines.append(line)
    
        if (
            processed_new_lines
            and end < len(lines)
            and not processed_new_lines[-1].endswith("\n")
        ):
            processed_new_lines[-1] += "\n"
    
        before = lines[: start - 1]
        after = lines[end:]
        modified_lines = before + processed_new_lines + after
        diff_result = generate_diff_preview(lines, modified_lines, start, end)
        error = None
        if self.current_file_path.endswith(".py"):
            full_content = "".join(modified_lines)
            try:
                black.format_file_contents(
                    full_content,
                    fast=True,
                    mode=black.Mode(),
                )
            except black.InvalidInput as e:
                error = {
                    "error": f"Python syntax error: {str(e)}",
                    "diff_lines": diff_result,
                    "auto_cancel": self.fail_on_python_syntax_error,
                }
            except Exception as e:
                if not isinstance(e, NothingChanged):
                    error = {
                        "error": f"Black check raised {type(e)}: {str(e)}",
                        "diff_lines": diff_result,
                    }
    
        elif self.enable_js_syntax_check and self.current_file_path.endswith(
            (".jsx", ".js")
        ):
            with tempfile.NamedTemporaryFile(
                mode="w", suffix=".jsx", delete=False
            ) as temp:
                temp_path = temp.name
                temp.writelines(modified_lines)
    
            try:
                presets = (
                    ["@babel/preset-react"]
                    if self.current_file_path.endswith(".jsx")
                    else ["@babel/preset-env"]
                )
    
                cmd = [
                    "npx",
                    "babel",
                    "--presets",
                    ",".join(presets),
                    "--no-babelrc",
                    temp_path,
                    "--out-file",
                    "/dev/null",  # Output to nowhere, we just want to check syntax
                ]
    
                # Execute Babel to transform (which validates syntax)
                process = subprocess.run(cmd, capture_output=True, text=True)
    
                if process.returncode != 0:
                    error_output = process.stderr
    
                    filtered_lines = []
                    for line in error_output.split("\n"):
                        if "node_modules/@babel" not in line:
                            filtered_lines.append(line)
    
                    filtered_error = "\n".join(filtered_lines).strip()
    
                    if not filtered_error:
                        filtered_error = "JavaScript syntax error detected"
    
                    error = {
                        "error": f"JavaScript syntax error: {filtered_error}",
                        "diff_lines": diff_result,
                        "auto_cancel": self.fail_on_js_syntax_error,
                    }
    
            except Exception as e:
                os.unlink(temp_path)
                error = {
                    "error": f"Error checking JavaScript syntax: {str(e)}",
                    "diff_lines": diff_result,
                }
    
            finally:
                if os.path.exists(temp_path):
                    os.unlink(temp_path)
    
        self.pending_modified_lines = modified_lines
        self.pending_diff = diff_result
    
        result = {
            "status": "preview",
            "message": "Changes ready to apply. Use confirm() to apply or cancel() to discard.",
            "diff_lines": diff_result["diff_lines"],
            "start": start,
            "end": end,
        }
        if error:
            result.update(error)
            if error.get("auto_cancel", False):
                self.pending_modified_lines = None
                self.pending_diff = None
                result["status"] = "auto_cancelled"
                result["message"] = (
                    "Changes automatically cancelled due to syntax error. The lines are still selected."
                )
            else:
                result["message"] += (
                    " It looks like there is a syntax error, but you can choose to fix it in the subsequent edits."
                )
    
        return result
  • Helper function to compute a unique identifier for selected text content, combining optional line range prefix with truncated SHA-256 hash. Used in 'overwrite' for verifying that the selected content has not changed since selection.
    def calculate_id(text: str, start: int = None, end: int = None) -> str:
        """
        Calculate a unique ID for content verification based on the text content.
    
        The ID is formed by combining a line prefix (if line numbers are provided)
        with a truncated SHA-256 id of the content. This allows quick verification
        that content hasn't changed between operations.
    
        Args:
            text (str): Content to generate ID for
            start (Optional[int]): Starting line number for the content
            end (Optional[int]): Ending line number for the content
        Returns:
            str: ID string in format: [LinePrefix]-[Truncatedid]
                 Example: "L10-15-a7" for content spanning lines 10-15
                 Example: "L5-b3" for content on line 5 only
        """
        prefix = ""
        if start and end:
            prefix = f"L{start}-{end}-"
            if start == end:
                prefix = f"L{start}-"
    
        return f"{prefix}{hashlib.sha256(text.encode()).hexdigest()[:2]}"
  • Helper function to generate a context-aware diff preview of proposed changes in 'overwrite' tool, showing removed (+/- prefixed), added lines, and surrounding context lines.
    def generate_diff_preview(
        original_lines: list, modified_lines: list, start: int, end: int
    ) -> dict:
        """
        Generate a diff preview comparing original and modified content.
    
        Args:
            original_lines (list): List of original file lines
            modified_lines (list): List of modified file lines
            start (int): Start line number of the edit (1-based)
            end (int): End line number of the edit (1-based)
    
        Returns:
            dict: A dictionary with keys prefixed with + or - to indicate additions/deletions
                  Format: [("-1", "removed line"), ("+1", "added line")]
        """
        diffs = []
        # Add some context lines before the change
        context_start = max(0, start - 1 - 3)  # 3 lines of context before
        for i in range(context_start, start - 1):
            diffs.append((i + 1, original_lines[i].rstrip()))
        # Show removed lines
        for i in range(start - 1, end):
            diffs.append((f"-{i+1}", original_lines[i].rstrip()))
    
        # Show added lines
        new_content = "".join(
            modified_lines[
                start - 1 : start
                - 1
                + len(modified_lines)
                - len(original_lines)
                + (end - (start - 1))
            ]
        )
        new_lines = new_content.splitlines()
        for i, line in enumerate(new_lines):
            diffs.append((f"+{start+i}", line))
        context_end = min(len(original_lines), end + 3)  # 3 lines of context after
        for i in range(end, context_end):
            diffs.append((i + 1, original_lines[i].rstrip()))
        return {
            "diff_lines": diffs,
        }
  • The FastMCP decorator registering the overwrite handler function as a tool.
    async def overwrite(

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/danielpodrazka/editor-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server