Help us shape the future of Go! Take the 2026 Go Developer Survey

fstools

package
v0.18.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package fstools implements the correctness- and security-critical filesystem tool bodies of the mecatl kit — Read, ListDir, Edit, Write, Copy, Move, Remove, Grep, Glob, and an OPTIONAL Shell — as tool.Tool values executing against an injected tool.Workspace (and, for Shell, an injected tool.CommandRunner). It travels WITH the importable engine module so an external consumer of engine/agent gets these tools — and their enforced invariants — by import, not by re-deriving them:

  • Edit's three invariants: read-before-edit (+ unchanged-since), exact match, and uniqueness-unless-replace_all.
  • Write's read-before-overwrite (Edit invariant #1 for existing files).
  • Read/Grep/Glob output caps with clear truncation markers.
  • Shell's partial-output-preserving timeout/cancel handling and the trailer that always survives the output cap.

These bodies depend only on engine/session + engine/tool (+ stdlib). They never touch the real OS: the filesystem tools go through the tool.Workspace and optional tool.WorkspaceNamespace seams, and Shell goes through the injected tool.CommandRunner — so a consumer picks the FileSystem/Workspace and shell backend. The reference in-memory Workspace is engine/adapter/memfs; the honest no-op is engine/adapter/nofs.

Opt-in / opt-out

The catalog is composed, not fixed. A consumer may:

  • take everything: register All() (Read/ListDir/Edit/Write/Copy/Move/Remove/ Grep/Glob) via Register, then add NewShellTool() only when a shell is configured;
  • take a subset: register only the tool values it wants;
  • swap a tool by name: register its own Tool under the same Spec().Name in place of one of these;
  • ignore the package entirely and supply its own tools.

Shell is deliberately NOT in All(): it needs a tool.CommandRunner and command execution is optional. A deployment with no shell simply never constructs one.

Recoverable vs harness errors

Each tool parses its session.ToolCall.Args (JSON), runs against the seam, and returns a session.ToolResult. Recoverable, model-addressable failures (a missing argument, a failed Edit invariant, a non-existent file) are returned as an *error* ToolResult via session.NewToolError so the model can read and recover from them; the Go error return is reserved for harness-level faults the model cannot act on.

Every tool carries a documentation-quality ToolSpec.Description (the model's onboarding manual, gauntlet #10) and a correct ReadOnly() value, which drives the agent loop's read-parallel / mutate-serial dispatch (gauntlet #4).

Output cap

MaxOutputBytes (25,000 bytes) mirrors internal/adapter/toolkit.MaxOutputBytes EXACTLY — keep those two byte-identical. It is redefined locally (not imported) because engine/ is its own module and toolkit lives under the host repo's internal/adapter tree, which the engine module must not import. The related domain bound engine/session.MaxToolResultTextBytes (25 KiB = 25,600 bytes) is a slightly LARGER upper bound on any tool-result text block that these tool caps sit under — deliberately not byte-identical.

Index

Constants

View Source
const MaxOutputBytes = 25_000

MaxOutputBytes caps the byte length of a single tool's textual result. It is the fstools output cap; tools append a truncation marker (see truncate) when they trim to it. It mirrors internal/adapter/toolkit.MaxOutputBytes EXACTLY (both 25,000 bytes); the domain block bound engine/session.MaxToolResultTextBytes (25 KiB = 25,600 bytes) is a larger upper bound these caps sit under.

View Source
const ShellToolName = tool.ShellToolName

ShellToolName aliases tool.ShellToolName, the single authority for the name the Shell tool registers under (the permission evaluator special-cases the literal, so the constant lives in the port package where every implementation — this adapter's AND engine/agent's background-capable ShellTool — can import it). Callers probe the catalog for Shell enablement by referencing the constant rather than a local literal that could drift on a rename (see internal/adapter/server.Service.capabilities).

View Source
const TruncationMarker = "\n... [output truncated: exceeded 25000 bytes]"

TruncationMarker is the suffix truncate appends when it trims a body to the byte cap. It is exported so a caller that must reserve room for content AFTER a truncated body (Shell's timeout/cancel trailer, which must survive the cap) can account for the marker's length without hard-coding the literal.

Variables

This section is empty.

Functions

func All

func All() []tool.Tool

All returns the always-available filesystem tools as a fresh slice, in the canonical catalog order. Shell is NOT included: it requires a tool.CommandRunner and is optional — add it separately via NewShellTool when a runner is configured.

func NewShellTool added in v0.15.0

func NewShellTool() tool.Tool

NewShellTool constructs the Shell tool. The runner is NOT captured here — it is read off the tool.Environment at Execute time (issue #462). The composition root registers the returned tool ONLY when a runner is available for the namespace; without one, the catalog has no Shell and the agent runs shell-less.

func Register

func Register(cat *tool.Catalog) error

Register adds the always-available filesystem tools (everything in All(), i.e. NOT Shell) to cat. It returns the first registration error (e.g. a name collision) encountered, or nil on success. To enable command execution, additionally register NewShellTool(), e.g. cat.MustRegister(fstools.NewShellTool()).

Types

type CopyTool added in v0.15.0

type CopyTool struct{}

CopyTool copies one regular file to an absent destination.

func (CopyTool) Execute added in v0.15.0

Execute copies a regular file without overwriting an existing destination.

func (CopyTool) ReadOnly added in v0.15.0

func (CopyTool) ReadOnly() bool

ReadOnly reports that Copy mutates the workspace.

func (CopyTool) Spec added in v0.15.0

func (CopyTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Copy tool.

type EditTool

type EditTool struct{}

EditTool replaces an exact substring in a file, enforcing the three Edit invariants (read-before-edit, exact-match, uniqueness) against the Workspace read-ledger. It mutates state, so ReadOnly is false.

func (EditTool) Execute

Execute enforces the three Edit invariants and writes the modified file via a conditional replace.

func (EditTool) ReadOnly

func (EditTool) ReadOnly() bool

ReadOnly reports that Edit mutates state.

func (EditTool) Spec

func (EditTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Edit tool.

type GlobTool

type GlobTool struct{}

GlobTool lists files matching a glob pattern. It does not mutate state, so ReadOnly is true.

func (GlobTool) Execute

Execute runs the glob and returns capped, sorted paths.

func (GlobTool) ReadOnly

func (GlobTool) ReadOnly() bool

ReadOnly reports that Glob does not mutate state.

func (GlobTool) Spec

func (GlobTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Glob tool.

type GrepTool

type GrepTool struct{}

GrepTool searches file contents for a regular expression. It does not mutate state, so ReadOnly is true.

func (GrepTool) Execute

Execute runs the search and returns capped, formatted matches.

func (GrepTool) ReadOnly

func (GrepTool) ReadOnly() bool

ReadOnly reports that Grep does not mutate state.

func (GrepTool) Spec

func (GrepTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Grep tool.

type ListDirTool added in v0.15.0

type ListDirTool struct{}

ListDirTool lists the immediate children of one directory.

func (ListDirTool) Execute added in v0.15.0

Execute lists the immediate children of a directory.

func (ListDirTool) ReadOnly added in v0.15.0

func (ListDirTool) ReadOnly() bool

ReadOnly reports that ListDir does not mutate the workspace.

func (ListDirTool) Spec added in v0.15.0

func (ListDirTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the ListDir tool.

type MoveTool added in v0.15.0

type MoveTool struct{}

MoveTool moves a file or directory to an absent destination.

func (MoveTool) Execute added in v0.15.0

Execute moves a path without overwriting an existing destination.

func (MoveTool) ReadOnly added in v0.15.0

func (MoveTool) ReadOnly() bool

ReadOnly reports that Move mutates the workspace.

func (MoveTool) Spec added in v0.15.0

func (MoveTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Move tool.

type ReadTool

type ReadTool struct{}

ReadTool reads a file and returns its contents with 1-based line-number prefixes, recording the read in the Workspace ledger so Edit's read-before-edit invariant can later be satisfied.

func (ReadTool) Execute

Execute reads the file, returns it with line-number prefixes, and records the read in the Workspace ledger.

func (ReadTool) ReadOnly

func (ReadTool) ReadOnly() bool

ReadOnly reports that Read does not mutate state.

func (ReadTool) Spec

func (ReadTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Read tool.

type RemoveTool added in v0.15.0

type RemoveTool struct{}

RemoveTool removes one file or empty physical directory non-recursively.

func (RemoveTool) Execute added in v0.15.0

Execute removes one path non-recursively.

func (RemoveTool) ReadOnly added in v0.15.0

func (RemoveTool) ReadOnly() bool

ReadOnly reports that Remove mutates the workspace.

func (RemoveTool) Spec added in v0.15.0

func (RemoveTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Remove tool.

type ShellTool added in v0.15.0

type ShellTool struct{}

ShellTool runs a shell command via the CommandRunner bound to the tool.Environment it executes against (issue #462). It is statically classified as non-read-only: deciding whether a specific command is read-only is governance's job, not this tool's.

The runner is read off the Environment at Execute time, NOT captured at construction: a bound runner is part of the per-namespace Environment (main session, or a forked child whose runner is bound to the child namespace), so the command's cwd always matches the workspace the tool executes against, never a stale shared parent base. A namespace with no shell (env.CommandRunner == nil) surfaces ErrNoShell honestly rather than aborting. The composition root decides whether to REGISTER a Shell tool at all based on runner availability; a shell-less catalog simply omits Shell.

Residual: this fixes the runner's working DIRECTORY, not Shell's trust model. Unlike path-scoped Edit/Write (confined by os.Root), Shell can still escape its cwd via absolute paths or `cd` — that is inherent to running a shell, the same as in the main session. The fix removes the ACCIDENTAL shared-base mutation (a fork branch's relative-path Shell landing in the parent base), which is what ParallelTool.ReadOnly() / the read-only-share / mutating-fork isolation needs.

func (ShellTool) Execute added in v0.15.0

Execute runs the command, honoring an optional timeout, and returns combined output with the exit code. The runner is read off env; a shell-less namespace (nil runner) surfaces ErrNoShell.

func (ShellTool) ReadOnly added in v0.15.0

func (ShellTool) ReadOnly() bool

ReadOnly reports that Shell is statically treated as mutating.

func (ShellTool) Spec added in v0.15.0

func (ShellTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Shell tool.

type WriteTool

type WriteTool struct{}

WriteTool writes a file. Creating a new file needs no prior read; overwriting an existing file requires a read-before-overwrite, mirroring Edit invariant #1. It mutates state, so ReadOnly is false.

func (WriteTool) Execute

Execute writes the file, enforcing read-before-overwrite on existing paths via the version protocol: a NEW file uses create-only; an EXISTING file requires a recorded version, re-reads the current version, and finishes with a conditional replace against that current version. No unconditional operation is used by the agent-facing Write tool.

func (WriteTool) ReadOnly

func (WriteTool) ReadOnly() bool

ReadOnly reports that Write mutates state.

func (WriteTool) Spec

func (WriteTool) Spec() tool.ToolSpec

Spec returns the model-facing specification of the Write tool.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL