// Force strict mode and setup for ESM
"use strict";
import {
  StructuredToolError,
  checkPriorRead
} from "./chunk-GJHMAWS7.js";
import {
  CommitAttributionService,
  DEFAULT_DIFF_OPTIONS,
  FileEncoding,
  createPatch,
  detectLineEnding,
  getDiffStat,
  needsUtf8Bom
} from "./chunk-PSIHICBU.js";
import "./chunk-24YKA2DA.js";
import "./chunk-O4PICXES.js";
import "./chunk-TW522KN6.js";
import "./chunk-TI4GXJKO.js";
import "./chunk-AKBFRR6J.js";
import "./chunk-MLZQVCF3.js";
import "./chunk-EMVEDSVZ.js";
import "./chunk-EY6BDW7Y.js";
import "./chunk-HAQCNXSG.js";
import "./chunk-77WXWU44.js";
import "./chunk-GVWPJCXU.js";
import {
  FileOperationEvent,
  fileExists,
  getLanguageFromFilePath,
  getSpecificMimeType,
  logFileOperation
} from "./chunk-G5EEAVVW.js";
import "./chunk-J5VCSWPA.js";
import "./chunk-UWCTAVOD.js";
import "./chunk-W57YDFU5.js";
import "./chunk-OFEVLU4C.js";
import {
  isAutoMemPath
} from "./chunk-NQ3E7YLD.js";
import "./chunk-ODPVJ6JJ.js";
import "./chunk-UQRYJQBE.js";
import {
  ToolDisplayNames,
  ToolNames
} from "./chunk-6RQTH7UQ.js";
import {
  BaseDeclarativeTool,
  BaseToolInvocation
} from "./chunk-R2B65CAN.js";
import "./chunk-7YJIR2FX.js";
import "./chunk-OIL7KDWV.js";
import "./chunk-U2K6HDUJ.js";
import "./chunk-6NUSWV4M.js";
import "./chunk-HCSJIOLR.js";
import "./chunk-SZOEIL6S.js";
import "./chunk-5IFG2VC4.js";
import {
  createDebugLogger,
  getErrorMessage,
  isNodeError,
  makeRelative,
  shortenPath,
  unescapePath
} from "./chunk-ACBGEKB7.js";
import "./chunk-ZERZSAZL.js";
import "./chunk-QN5NZ3UQ.js";
import "./chunk-BR4QREVK.js";
import "./chunk-QWSRH265.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/core/src/tools/write-file.ts
init_esbuild_shims();
import fs from "node:fs";
import path from "node:path";
var debugLogger = createDebugLogger("WRITE_FILE");
var WriteFileToolInvocation = class extends BaseToolInvocation {
  constructor(config, params) {
    super(params);
    this.config = config;
  }
  static {
    __name(this, "WriteFileToolInvocation");
  }
  toolLocations() {
    return [{ path: this.params.file_path }];
  }
  getDescription() {
    const relativePath = makeRelative(
      this.params.file_path,
      this.config.getTargetDir()
    );
    return `Writing to ${shortenPath(relativePath)}`;
  }
  /**
   * Write operations always need user confirmation, except for managed
   * auto-memory files which are written autonomously by the model.
   */
  async getDefaultPermission() {
    const projectRoot = this.config.getProjectRoot();
    if (isAutoMemPath(path.resolve(this.params.file_path), projectRoot)) {
      return "allow";
    }
    return "ask";
  }
  /**
   * Constructs the write-file diff confirmation details.
   */
  async getConfirmationDetails(_abortSignal) {
    let originalContent = "";
    let fileExists2 = await fileExists(this.params.file_path);
    if (!this.config.getFileReadCacheDisabled()) {
      const decision = await checkPriorRead(
        this.config.getFileReadCache(),
        this.params.file_path,
        "overwriting"
      );
      if (!decision.ok) {
        throw new StructuredToolError(decision.rawMessage, decision.type);
      }
    }
    if (fileExists2) {
      try {
        const { content } = await this.config.getFileSystemService().readTextFile({ path: this.params.file_path });
        originalContent = content;
      } catch (err) {
        if (isNodeError(err) && err.code === "ENOENT") {
          fileExists2 = false;
        } else {
          throw new Error(
            `Error reading existing file for confirmation: ${getErrorMessage(err)}`
          );
        }
      }
    }
    if (fileExists2 && !this.config.getFileReadCacheDisabled()) {
      const postDecision = await checkPriorRead(
        this.config.getFileReadCache(),
        this.params.file_path,
        "overwriting",
        { expectExisting: true }
      );
      if (!postDecision.ok) {
        debugLogger.warn("post-read TOCTOU rejection (confirmation)", {
          path: this.params.file_path,
          reason: postDecision.type
        });
        throw new StructuredToolError(
          postDecision.rawMessage,
          postDecision.type
        );
      }
    }
    const relativePath = makeRelative(
      this.params.file_path,
      this.config.getTargetDir()
    );
    const fileName = path.basename(this.params.file_path);
    const fileDiff = createPatch(
      fileName,
      originalContent,
      // Original content (empty if new file or unreadable)
      this.params.content,
      // Content after potential correction
      "Current",
      "Proposed",
      DEFAULT_DIFF_OPTIONS
    );
    const confirmationDetails = {
      type: "edit",
      title: `Confirm Write: ${shortenPath(relativePath)}`,
      fileName,
      filePath: this.params.file_path,
      fileDiff,
      originalContent,
      newContent: this.params.content,
      onConfirm: /* @__PURE__ */ __name(async (outcome) => {
        if (outcome === "proceed_always" /* ProceedAlways */) {
          this.config.setApprovalMode("auto-edit" /* AUTO_EDIT */);
        }
      }, "onConfirm")
    };
    return confirmationDetails;
  }
  async execute(_abortSignal) {
    const { file_path, content, ai_proposed_content, modified_by_user } = this.params;
    let fileExists2 = await fileExists(file_path);
    let originalContent = "";
    let useBOM = false;
    let detectedEncoding;
    let detectedLineEnding;
    const dirName = path.dirname(file_path);
    if (!this.config.getFileReadCacheDisabled()) {
      const decision = await checkPriorRead(
        this.config.getFileReadCache(),
        file_path,
        "overwriting"
      );
      if (!decision.ok) {
        return {
          llmContent: decision.rawMessage,
          returnDisplay: `Error: ${decision.displayMessage}`,
          error: {
            message: decision.rawMessage,
            type: decision.type
          }
        };
      }
    }
    if (fileExists2) {
      try {
        const fileInfo = await this.config.getFileSystemService().readTextFile({ path: file_path });
        if (fileInfo._meta?.bom !== void 0) {
          useBOM = fileInfo._meta.bom;
        } else {
          useBOM = fileInfo.content.length > 0 && fileInfo.content.codePointAt(0) === 65279;
        }
        detectedEncoding = fileInfo._meta?.encoding || "utf-8";
        detectedLineEnding = detectLineEnding(fileInfo.content);
        originalContent = fileInfo.content;
        fileExists2 = true;
      } catch (err) {
        if (isNodeError(err) && err.code === "ENOENT") {
          fileExists2 = false;
        } else {
          const error = {
            message: getErrorMessage(err),
            code: isNodeError(err) ? err.code : void 0
          };
          const errorMsg = error.code ? `Error checking existing file '${file_path}': ${error.message} (${error.code})` : `Error checking existing file: ${error.message}`;
          return {
            llmContent: errorMsg,
            returnDisplay: errorMsg,
            error: {
              message: errorMsg,
              type: "file_write_failure" /* FILE_WRITE_FAILURE */
            }
          };
        }
      }
    }
    if (fileExists2 && !this.config.getFileReadCacheDisabled()) {
      const postDecision = await checkPriorRead(
        this.config.getFileReadCache(),
        file_path,
        "overwriting",
        { expectExisting: true }
      );
      if (!postDecision.ok) {
        debugLogger.warn("post-read TOCTOU rejection (execute)", {
          path: file_path,
          reason: postDecision.type
        });
        return {
          llmContent: postDecision.rawMessage,
          returnDisplay: `Error: ${postDecision.displayMessage}`,
          error: {
            message: postDecision.rawMessage,
            type: postDecision.type
          }
        };
      }
    }
    if (!fileExists2) {
      const userEncoding = this.config.getDefaultFileEncoding();
      if (userEncoding === FileEncoding.UTF8_BOM) {
        useBOM = true;
      } else if (userEncoding === void 0) {
        useBOM = needsUtf8Bom(file_path);
      }
      detectedEncoding = void 0;
    }
    try {
      await this.config.getFileHistoryService().trackEdit(file_path);
    } catch {
    }
    if (!this.config.getFileReadCacheDisabled()) {
      const writeDecision = await checkPriorRead(
        this.config.getFileReadCache(),
        file_path,
        "overwriting",
        // If the file existed when we read it (`fileExists` is still
        // true after readTextFile), ENOENT here means the original
        // target disappeared between the post-read check and now —
        // reject rather than fall through and silently re-create the
        // file from stale bytes. For new-file creation
        // (`fileExists === false`), ENOENT is the expected pre-write
        // state (ok:true → writeTextFile creates).
        { expectExisting: fileExists2 }
      );
      if (!writeDecision.ok) {
        debugLogger.warn("pre-write TOCTOU rejection", {
          path: file_path,
          reason: writeDecision.type
        });
        return {
          llmContent: writeDecision.rawMessage,
          returnDisplay: `Error: ${writeDecision.displayMessage}`,
          error: {
            message: writeDecision.rawMessage,
            type: writeDecision.type
          }
        };
      }
    }
    if (!fileExists2) {
      fs.mkdirSync(dirName, { recursive: true });
    }
    try {
      await this.config.getFileSystemService().writeTextFile({
        path: file_path,
        content,
        _meta: {
          bom: useBOM,
          encoding: detectedEncoding,
          lineEnding: detectedLineEnding
        }
      });
      if (!modified_by_user) {
        CommitAttributionService.getInstance().recordEdit(
          file_path,
          fileExists2 ? originalContent : null,
          content
        );
      }
      try {
        const postWriteStats = fs.statSync(file_path);
        this.config.getFileReadCache().recordWrite(file_path, postWriteStats);
      } catch {
      }
      const fileName = path.basename(file_path);
      const currentContentForDiff = originalContent;
      const fileDiff = createPatch(
        fileName,
        currentContentForDiff,
        content,
        "Original",
        "Written",
        DEFAULT_DIFF_OPTIONS
      );
      const originallyProposedContent = ai_proposed_content || content;
      const diffStat = getDiffStat(
        fileName,
        currentContentForDiff,
        originallyProposedContent,
        content
      );
      const llmSuccessMessageParts = [
        !fileExists2 ? `Successfully created and wrote to new file: ${file_path}.` : `Successfully overwrote file: ${file_path}.`
      ];
      if (modified_by_user) {
        llmSuccessMessageParts.push(
          `User modified the \`content\` to be: ${content}`
        );
      }
      const mimetype = getSpecificMimeType(file_path);
      const programmingLanguage = getLanguageFromFilePath(file_path);
      const extension = path.extname(file_path);
      const operation = !fileExists2 ? "create" /* CREATE */ : "update" /* UPDATE */;
      const lineCount = content.split("\n").length;
      logFileOperation(
        this.config,
        new FileOperationEvent(
          WriteFileTool.Name,
          operation,
          lineCount,
          mimetype,
          extension,
          programmingLanguage
        )
      );
      const displayResult = {
        fileDiff,
        fileName,
        originalContent,
        newContent: content,
        diffStat
      };
      return {
        llmContent: llmSuccessMessageParts.join(" "),
        returnDisplay: displayResult
      };
    } catch (error) {
      let errorMsg;
      let errorType = "file_write_failure" /* FILE_WRITE_FAILURE */;
      if (isNodeError(error)) {
        errorMsg = `Error writing to file '${file_path}': ${error.message} (${error.code})`;
        if (error.code === "EACCES") {
          errorMsg = `Permission denied writing to file: ${file_path} (${error.code})`;
          errorType = "permission_denied" /* PERMISSION_DENIED */;
        } else if (error.code === "ENOSPC") {
          errorMsg = `No space left on device: ${file_path} (${error.code})`;
          errorType = "no_space_left" /* NO_SPACE_LEFT */;
        } else if (error.code === "EISDIR") {
          errorMsg = `Target is a directory, not a file: ${file_path} (${error.code})`;
          errorType = "target_is_directory" /* TARGET_IS_DIRECTORY */;
        }
        if (this.config.getDebugMode() && error.stack) {
          debugLogger.debug("Write file error stack:", error.stack);
        }
      } else if (error instanceof Error) {
        errorMsg = `Error writing to file: ${error.message}`;
      } else {
        errorMsg = `Error writing to file: ${String(error)}`;
      }
      return {
        llmContent: errorMsg,
        returnDisplay: errorMsg,
        error: {
          message: errorMsg,
          type: errorType
        }
      };
    }
  }
};
var WriteFileTool = class _WriteFileTool extends BaseDeclarativeTool {
  constructor(config) {
    super(
      _WriteFileTool.Name,
      ToolDisplayNames.WRITE_FILE,
      `Writes content to a specified file in the local filesystem.

      The user has the ability to modify \`content\`. If modified, this will be stated in the response.`,
      "edit" /* Edit */,
      {
        properties: {
          file_path: {
            description: "The absolute path to the file to write to (e.g., '/home/user/project/file.txt'). Relative paths are not supported.",
            type: "string"
          },
          content: {
            description: "The content to write to the file.",
            type: "string"
          }
        },
        required: ["file_path", "content"],
        type: "object"
      }
    );
    this.config = config;
  }
  static {
    __name(this, "WriteFileTool");
  }
  static Name = ToolNames.WRITE_FILE;
  validateToolParamValues(params) {
    const filePath = unescapePath(params.file_path.trim());
    params.file_path = filePath;
    if (!filePath) {
      return `Missing or empty "file_path"`;
    }
    if (!path.isAbsolute(filePath)) {
      return `File path must be absolute: ${filePath}`;
    }
    try {
      if (fs.existsSync(filePath)) {
        const stats = fs.lstatSync(filePath);
        if (stats.isDirectory()) {
          return `Path is a directory, not a file: ${filePath}`;
        }
      }
    } catch (statError) {
      return `Error accessing path properties for validation: ${filePath}. Reason: ${statError instanceof Error ? statError.message : String(statError)}`;
    }
    return null;
  }
  createInvocation(params) {
    return new WriteFileToolInvocation(this.config, params);
  }
  toAutoClassifierInput(params) {
    const content = params.content ?? "";
    return {
      file_path: params.file_path,
      byte_count: Buffer.byteLength(content, "utf8"),
      content_preview: content.slice(0, 300),
      content_truncated: content.length > 300
    };
  }
  getModifyContext(_abortSignal) {
    return {
      getFilePath: /* @__PURE__ */ __name((params) => params.file_path, "getFilePath"),
      getCurrentContent: /* @__PURE__ */ __name(async (params) => {
        const fileExists2 = await fileExists(params.file_path);
        if (fileExists2) {
          try {
            const { content } = await this.config.getFileSystemService().readTextFile({ path: params.file_path });
            return content;
          } catch (err) {
            if (!isNodeError(err) || err.code !== "ENOENT") throw err;
            return "";
          }
        } else {
          return "";
        }
      }, "getCurrentContent"),
      getProposedContent: /* @__PURE__ */ __name(async (params) => params.content, "getProposedContent"),
      createUpdatedParams: /* @__PURE__ */ __name((_oldContent, modifiedProposedContent, originalParams) => {
        const content = originalParams.content;
        return {
          ...originalParams,
          ai_proposed_content: content,
          content: modifiedProposedContent,
          modified_by_user: true
        };
      }, "createUpdatedParams")
    };
  }
};
export {
  WriteFileTool
};
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
