Skip to content

Refactor read, write and edit tools in pi #165

Description

@tenshiak

Description

Currently there are multiple ways of providing filepaths, multiple ways of calling the edit tool, and that makes models confused. Even GPT 5.6 has issues with using the edits[] array and often calls the edit tool multiple times to edit the same file. There should be only one way to do things correctly.

My proposal is to change the read and edit params to this. It's from the pi's package. I used filePath for read because it's the default for all other AFT tools, and read was always the outlier, using path instead. It could be the reason of some of the model's confusion that tried to use it with filePath before #148.

const ReadParams = Type.Object({
  filePath: Type.Optional(
    Type.String({
      description: "Path to the file to read (absolute or relative to project root)",
    }),
  ),
  offset: optionalInt(
    1,
    Number.MAX_SAFE_INTEGER,
    "1-based line number to start reading from (use with limit)",
  ),
  limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "Maximum number of lines to return"),
});

const WriteParams = Type.Object({
  filePath: Type.Optional(
    Type.String({
      description: "Path to the file to write (absolute or relative to project root)",
    }),
  ),
  content: Type.String({ description: "Full file contents to write" }),
});

const BatchEditParams = Type.Object({
  oldString: Type.Optional(
    Type.String({ description: "Text to find for a batch find/replace edit" }),
  ),
  newString: Type.Optional(
    Type.String({ description: "Replacement text for a batch find/replace edit" }),
  ),
  replaceAll: Type.Optional(
    Type.Boolean({ description: "Replace every occurrence for this batch item" }),
  ),
  occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER, "0-based occurrence for this batch item"),
  startLine: optionalInt(
    1,
    Number.MAX_SAFE_INTEGER,
    "1-based start line for a batch line-range edit",
  ),
  endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based end line for a batch line-range edit"),
  content: Type.Optional(
    Type.String({
      description: "Replacement text for a batch line-range edit (empty string deletes the lines)",
    }),
  ),
});

const EditParams = Type.Object({
  filePath: Type.Optional(
    Type.String({
      description: "Path to the file to edit (absolute or relative to project root)",
    }),
  ),
  appendContent: Type.Optional(
    Type.String({
      description:
        "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set, edits[] are ignored.",
    }),
  ),
  edits: Type.Optional(
    Type.Array(BatchEditParams, {
      description:
        "Batch edits — array of { oldString, newString }, { oldString, newString, replaceAll: true }, or { startLine, endLine, content } objects applied atomically.",
    }),
  ),
});

An optional filePath was added because many models used filePath instead of path (#148), but there is a simple way in pi to fix this. Pi has a built-in prepareArguments argument for registerTool that solves exactly this problem. It is a much better solution than copying parameters because it's mechanical, not relying on LLMs ability to call tools. Saves a smidge of tokens as well. Example usage of this function can be seen here in pi's own built-in edit tool I recommend implementing pi's fixes as well.
Just like with this example, it could also be used to fix other errors users report in the future, improving the success rate.

Maybe as an addition, I leave it for consideration: make the occurrence 1-based as well to standardize the indexing for all parameters. My models often fail on this, trying to call occurrence 1 when there is only one existing, because the occurrence is 0-based unlike other parameters.

Use case

It would save them turns & money because if there are multiple ways of doing things with tools, some models get confused and use them wrong.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions