fs

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 21, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package fs exposes filesystem tools (Read, Write, Edit) as stateless singletons. Construction policy (eager vs lazy) is decided by the agent; this package only knows how to produce tool instances.

Index

Constants

View Source
const (
	OpCreate    = "create"
	OpEdit      = "edit"
	OpOverwrite = "overwrite"
)

Op enumerates the kinds of file mutation a FileDiff describes. Kept as strings so the wire/UI side can render without importing this package.

View Source
const (
	LineContext = "context"
	LineAdd     = "add"
	LineRemove  = "remove"
)

LineKind enumerates how a single DiffLine relates to the change. Same semantics as a unified diff's prefix character.

View Source
const ContextLines = 3

ContextLines is how many unchanged lines we include above and below an edit hunk. Matches `diff -u` default.

View Source
const DefaultReadLimit = 2000

DefaultReadLimit caps an unbounded Read at this many lines. The model can pass an explicit larger limit when it really needs more, but the default protects the context window from accidental 50k-line dumps. Matches Claude Code's MAX_LINES_TO_READ.

Variables

This section is empty.

Functions

func Names

func Names() []tools.ToolName

Names lists every tool name this package contributes, in canonical order.

Types

type DiffHunk

type DiffHunk struct {
	OldStart, OldCount int
	NewStart, NewCount int
	Lines              []DiffLine
}

DiffHunk groups consecutive related lines, mirroring unified-diff hunks. Start counts are 1-based; Count is the number of lines in that hunk from the respective side (matches the "@@ -OldStart,OldCount +NewStart,NewCount @@" header).

type DiffLine

type DiffLine struct {
	Kind string // context / add / remove
	Old  int    // 0 if not present on the old side
	New  int    // 0 if not present on the new side
	Text string
}

DiffLine carries one rendered row. Old and New are 1-based line numbers on each side; the one that doesn't apply (e.g. New on a "remove" line) is 0 so the UI can render an empty cell.

type EditTool

type EditTool struct {
	// contains filtered or unexported fields
}

func NewEdit

func NewEdit(tracker *ReadTracker, workdir string) *EditTool

func (*EditTool) Description

func (t *EditTool) Description() string

func (*EditTool) Execute

func (t *EditTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*EditTool) Name

func (t *EditTool) Name() string

func (*EditTool) Schema

func (t *EditTool) Schema() json.RawMessage

type FileDiff

type FileDiff struct {
	Path  string
	Op    string // create / edit / overwrite
	Hunks []DiffHunk
}

FileDiff is the structured payload write_file and edit_file attach to tools.Result.Metadata so UIs can render git-style diffs (red removes, green adds, line numbers in two columns) without parsing text.

LLMs never see this — Content carries the model-facing summary.

type GlobTool

type GlobTool struct {
	// contains filtered or unexported fields
}

func NewGlob

func NewGlob(workdir string) *GlobTool

func (*GlobTool) Description

func (t *GlobTool) Description() string

func (*GlobTool) Execute

func (t *GlobTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*GlobTool) Name

func (t *GlobTool) Name() string

func (*GlobTool) Schema

func (t *GlobTool) Schema() json.RawMessage

type ReadTool

type ReadTool struct {
	// contains filtered or unexported fields
}

func NewRead

func NewRead(tracker *ReadTracker, workdir string) *ReadTool

func (*ReadTool) Description

func (t *ReadTool) Description() string

func (*ReadTool) Execute

func (t *ReadTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*ReadTool) Name

func (t *ReadTool) Name() string

func (*ReadTool) Schema

func (t *ReadTool) Schema() json.RawMessage

type ReadTracker

type ReadTracker struct {
	// contains filtered or unexported fields
}

ReadTracker records every read_file call the agent makes so that edit_file / write_file can refuse to mutate a file whose on-disk state has drifted from what the model has in context.

Three failure modes the tracker protects against, mirroring Claude Code's FileReadTool ↔ FileEditTool contract:

  1. Never read — the model is editing blind. Reject.
  2. Read but mtime advanced on disk afterwards — another process, or the user, changed the file. Force a re-read so the model's old_string still matches.
  3. Partial-view read (offset / explicit limit) — the model only saw a slice and has no idea what surrounds the edit. Force a full re-read before mutating.

Zero value is NOT usable; call NewReadTracker.

func NewReadTracker

func NewReadTracker() *ReadTracker

func (*ReadTracker) CanEdit

func (t *ReadTracker) CanEdit(absPath string, currentMtime time.Time) (bool, string)

CanEdit reports whether an edit_file call against absPath is permitted given the file's current mtime. When false, reason is the model-facing explanation (mirrors ref TS FileEditTool wording).

func (*ReadTracker) CanWrite

func (t *ReadTracker) CanWrite(absPath string, currentMtime time.Time) (bool, string)

CanWrite reports whether write_file may overwrite absPath. Same gate as edit — overwriting a stale or partial-view read is the same hazard as editing one.

func (*ReadTracker) Forget

func (t *ReadTracker) Forget(absPath string)

Forget drops the entry for absPath. Used by tests; never called in production code.

func (*ReadTracker) Lookup

func (t *ReadTracker) Lookup(absPath string) (readEntry, bool)

Lookup returns the recorded entry, or zero+false if absPath has never been recorded.

func (*ReadTracker) Record

func (t *ReadTracker) Record(absPath string, mtime time.Time, partial bool)

Record stores that absPath was read at the given mtime, with the partial flag indicating whether the read covered only a slice of the file. HasReadOffset is left false — callers that represent an actual Read tool invocation should use RecordRead instead so the dedup check can tell them apart from Edit/Write post-mutation updates.

func (*ReadTracker) RecordRead

func (t *ReadTracker) RecordRead(absPath string, mtime time.Time, partial bool)

RecordRead is like Record but marks the entry as coming from an actual Read tool call. The Read tool's dedup check only fires for entries with HasReadOffset=true, so Edit/Write post-mutation updates (which call Record) don't cause spurious "File unchanged since last read" stubs.

type WriteTool

type WriteTool struct {
	// contains filtered or unexported fields
}

func NewWrite

func NewWrite(tracker *ReadTracker, workdir string) *WriteTool

func (*WriteTool) Description

func (t *WriteTool) Description() string

func (*WriteTool) Execute

func (t *WriteTool) Execute(ctx context.Context, logger *slog.Logger, input json.RawMessage) (tools.Result, error)

func (*WriteTool) Name

func (t *WriteTool) Name() string

func (*WriteTool) Schema

func (t *WriteTool) Schema() json.RawMessage

Jump to

Keyboard shortcuts

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