file

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: AGPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package file provides file operation tools for the Aleutian Trace CLI.

This package implements five core file tools:

  • Read: Read file contents with line numbers and pagination
  • Write: Create new files with atomic writes
  • Edit: Make surgical edits via old_string → new_string replacement
  • Glob: Find files by glob pattern
  • Grep: Search file contents with regex

All tools integrate with the tool registry and support the 2-LLM architecture where the micro LLM (Granite4) routes queries to appropriate tools.

Thread Safety: All tools are safe for concurrent use.

Index

Constants

View Source
const (
	// Read tool limits
	DefaultReadLimit   = 2000
	MaxReadLimit       = 10000
	MaxFileSizeBytes   = 10 * 1024 * 1024 // 10MB max file size for read
	MaxLineLengthChars = 2000

	// Write tool limits
	MaxWriteContentSize = 5 * 1024 * 1024 // 5MB max write content

	// Edit tool limits
	MaxEditFileSize = 10 * 1024 * 1024 // 10MB max file size for edit

	// Glob tool limits
	DefaultGlobLimit = 100
	MaxGlobLimit     = 1000
	MaxPatternLength = 256

	// Grep tool limits
	DefaultGrepLimit = 100
	MaxGrepLimit     = 500
	MaxContextLines  = 10
	MaxGrepPattern   = 1024
)
View Source
const MaxFuzzyErrors = 5

MaxFuzzyErrors is the maximum allowed edit distance for approximate matching.

View Source
const MaxTreeDepth = 10

MaxTreeDepth is the maximum allowed tree depth.

Variables

View Source
var (
	ErrNoMatch       = errors.New("old_string not found in file")
	ErrMultipleMatch = errors.New("old_string matches multiple times; use replace_all=true or provide more context")
	ErrFileNotRead   = errors.New("file must be read before editing")
	ErrConflict      = errors.New("file was modified externally since last read; re-read and retry")
)

Edit errors for specific failure modes.

View Source
var DefaultExclusions = []string{
	".git",
	"node_modules",
	"vendor",
	"__pycache__",
	".venv",
	"venv",
	".idea",
	".vscode",
	"dist",
	"build",
	".next",
	"target",
}

Default exclusion patterns for glob and grep operations.

View Source
var SensitivePaths = []string{
	"/etc/passwd",
	"/etc/shadow",
	"/etc/hosts",
	"/.ssh/",
	"/.gnupg/",
	"/.aws/credentials",
	"/.env",
	"/id_rsa",
	"/id_ed25519",
}

SensitivePaths contains paths that should never be written to.

Functions

func IsSensitivePath

func IsSensitivePath(path string) bool

IsSensitivePath checks if a path is sensitive and should not be written.

func RegisterFileTools

func RegisterFileTools(registry *tools.Registry, config *Config)

RegisterFileTools registers all file operation tools with the registry.

Description:

Registers all file tools (Read, Write, Edit, Glob, Grep, Diff, Tree, JSON)
with the provided tool registry. These tools require a Config that
specifies the working directory and allowed paths.

Inputs:

registry - The tool registry to register with
config - Configuration for file tools (working directory, allowed paths)

Example:

registry := tools.NewRegistry()
config := file.NewConfig("/path/to/project")
file.RegisterFileTools(registry, config)

Thread Safety: This function is safe to call once during initialization.

func ResolveAndValidatePath

func ResolveAndValidatePath(path string, config *Config) (string, error)

ResolveAndValidatePath resolves symlinks and validates the path is allowed.

func StaticFileToolDefinitions

func StaticFileToolDefinitions() []tools.ToolDefinition

StaticFileToolDefinitions returns tool definitions without requiring config.

Description:

Returns the definitions for all file tools. These can be used for
query classification without initializing the full tool system.
The definitions include tool names, descriptions, and parameter schemas.

Outputs:

[]tools.ToolDefinition - The static tool definitions.

Example:

defs := file.StaticFileToolDefinitions()
// Use defs for LLM tool routing

Thread Safety: This function is safe for concurrent use.

Types

type Config

type Config struct {
	// AllowedPaths is a list of paths that file operations are allowed in.
	// If empty, only the working directory and its subdirectories are allowed.
	AllowedPaths []string

	// WorkingDir is the current working directory.
	WorkingDir string

	// ReadTracking tracks which files have been read (for Edit validation).
	// Maps file path to read time (Unix milliseconds UTC).
	ReadTracking map[string]int64

	// ContentHashes stores content hashes for optimistic locking.
	// When a file is read, its hash is stored. During edit, we verify
	// the hash matches to detect external modifications.
	ContentHashes map[string]string

	// LSPReleaser coordinates file access with LSP servers.
	// Optional. If nil, no LSP coordination is performed.
	// Required for Windows to avoid "Access is denied" errors during atomic writes.
	LSPReleaser LSPReleaser

	// GraphRefresher provides synchronous graph updates after file writes.
	// Optional. If nil, no synchronous refresh is performed.
	// Recommended to prevent event storms and stale graph queries.
	GraphRefresher GraphRefresher
}

Config holds configuration for file tools.

func NewConfig

func NewConfig(workingDir string) *Config

NewConfig creates a new Config with the given working directory. The working directory is resolved to its real path (symlinks followed).

func (*Config) ClearContentHash

func (c *Config) ClearContentHash(path string)

ClearContentHash removes the stored hash for a file after successful write.

func (*Config) GetContentHash

func (c *Config) GetContentHash(path string) string

GetContentHash retrieves the stored content hash for a file. Returns empty string if no hash was stored.

func (*Config) IsPathAllowed

func (c *Config) IsPathAllowed(path string) bool

IsPathAllowed checks if a path is within allowed directories. The path is resolved through symlinks before checking.

func (*Config) MarkFileRead

func (c *Config) MarkFileRead(path string)

MarkFileRead records that a file has been read.

func (*Config) StoreContentHash

func (c *Config) StoreContentHash(path string, hash string)

StoreContentHash records the content hash of a file when read. This enables optimistic locking during edits.

func (*Config) WasFileRead

func (c *Config) WasFileRead(path string) bool

WasFileRead checks if a file was previously read.

type DiffParams

type DiffParams struct {
	// FileA is the first file to compare.
	FileA string `json:"file_a"`

	// FileB is the second file to compare.
	FileB string `json:"file_b"`

	// ContextLines is the number of context lines around changes.
	ContextLines int `json:"context_lines,omitempty"`
}

DiffParams defines parameters for the Diff tool.

func (*DiffParams) Validate

func (p *DiffParams) Validate() error

Validate checks that DiffParams are valid. Note: Relative paths are allowed and should be resolved by the tool against the working directory.

type DiffResult

type DiffResult struct {
	// Diff is the unified diff output.
	Diff string `json:"diff"`

	// LinesAdded is the number of lines added.
	LinesAdded int `json:"lines_added"`

	// LinesRemoved is the number of lines removed.
	LinesRemoved int `json:"lines_removed"`

	// FilesIdentical indicates if the files are the same.
	FilesIdentical bool `json:"files_identical"`
}

DiffResult contains the outcome of a Diff operation.

type DiffTool

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

DiffTool implements the Diff file comparison operation.

Thread Safety: DiffTool is safe for concurrent use.

func NewDiffTool

func NewDiffTool(config *Config) *DiffTool

NewDiffTool creates a new Diff tool with the given configuration.

func (*DiffTool) Category

func (t *DiffTool) Category() tools.ToolCategory

Category returns the tool category.

func (*DiffTool) Definition

func (t *DiffTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*DiffTool) Execute

func (t *DiffTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute compares two files and returns the diff.

func (*DiffTool) Name

func (t *DiffTool) Name() string

Name returns the tool name.

type EditError

type EditError struct {
	// Err is the underlying error.
	Err error

	// MatchCount is the number of matches found.
	MatchCount int

	// Suggestion provides guidance for fixing the error.
	Suggestion string
}

EditError provides detailed error information for edit failures.

func (*EditError) Error

func (e *EditError) Error() string

Error implements the error interface.

func (*EditError) Unwrap

func (e *EditError) Unwrap() error

Unwrap returns the underlying error.

type EditParams

type EditParams struct {
	// FilePath is the absolute path to the file to edit.
	FilePath string `json:"file_path"`

	// OldString is the exact text to replace.
	OldString string `json:"old_string"`

	// NewString is the replacement text.
	NewString string `json:"new_string"`

	// ReplaceAll replaces all occurrences if true.
	ReplaceAll bool `json:"replace_all,omitempty"`
}

EditParams defines parameters for the Edit tool.

func (*EditParams) Validate

func (p *EditParams) Validate() error

Validate checks that EditParams are valid. Note: Relative paths are allowed and should be resolved by the tool against the working directory.

type EditResult

type EditResult struct {
	// Success indicates if the edit succeeded.
	Success bool `json:"success"`

	// Replacements is the number of replacements made.
	Replacements int `json:"replacements"`

	// Diff is the unified diff of the changes.
	Diff string `json:"diff"`

	// Path is the absolute path of the edited file.
	Path string `json:"path"`
}

EditResult contains the outcome of an Edit operation.

type EditTool

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

EditTool implements the Edit file operation.

Thread Safety: EditTool is safe for concurrent use.

func NewEditTool

func NewEditTool(config *Config) *EditTool

NewEditTool creates a new Edit tool with the given configuration.

func (*EditTool) Category

func (t *EditTool) Category() tools.ToolCategory

Category returns the tool category.

func (*EditTool) Definition

func (t *EditTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*EditTool) Execute

func (t *EditTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute performs a surgical edit on a file.

func (*EditTool) Name

func (t *EditTool) Name() string

Name returns the tool name.

type FileInfo

type FileInfo struct {
	// Path is the absolute path to the file.
	Path string `json:"path"`

	// RelPath is the path relative to the search root.
	RelPath string `json:"rel_path,omitempty"`

	// Size is the file size in bytes.
	Size int64 `json:"size"`

	// ModTime is the last modification time (Unix milliseconds UTC).
	ModTime int64 `json:"mod_time"`

	// IsDir indicates if this is a directory.
	IsDir bool `json:"is_dir,omitempty"`
}

FileInfo contains metadata about a file.

type GlobParams

type GlobParams struct {
	// Pattern is the glob pattern to match (e.g., "**/*.go").
	Pattern string `json:"pattern"`

	// Path is the directory to search in. Defaults to current working directory.
	Path string `json:"path,omitempty"`

	// Limit is the maximum number of results. Defaults to DefaultGlobLimit.
	Limit int `json:"limit,omitempty"`
}

GlobParams defines parameters for the Glob tool.

func (*GlobParams) Validate

func (p *GlobParams) Validate() error

Validate checks that GlobParams are valid.

type GlobResult

type GlobResult struct {
	// Files is the list of matching files.
	Files []FileInfo `json:"files"`

	// Count is the total number of matches found.
	Count int `json:"count"`

	// Truncated indicates if results were truncated due to limit.
	Truncated bool `json:"truncated"`

	// SearchPath is the directory that was searched.
	SearchPath string `json:"search_path"`
}

GlobResult contains the outcome of a Glob operation.

type GlobTool

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

GlobTool implements the Glob file search operation.

Thread Safety: GlobTool is safe for concurrent use.

func NewGlobTool

func NewGlobTool(config *Config) *GlobTool

NewGlobTool creates a new Glob tool with the given configuration.

func (*GlobTool) Category

func (t *GlobTool) Category() tools.ToolCategory

Category returns the tool category.

func (*GlobTool) Definition

func (t *GlobTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*GlobTool) Execute

func (t *GlobTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute finds files matching the glob pattern.

func (*GlobTool) Name

func (t *GlobTool) Name() string

Name returns the tool name.

type GraphRefresher

type GraphRefresher interface {
	// RefreshFiles synchronously refreshes the graph for the given file paths.
	// Returns after the graph is updated. If an error occurs, the graph may
	// be partially updated but should not corrupt existing data.
	RefreshFiles(ctx context.Context, paths []string) error
}

GraphRefresher provides synchronous graph updates after file writes.

Description:

When the agent writes a file, the code graph becomes stale. Instead of
waiting for fsnotify to detect the change and trigger an asynchronous
refresh (which can cause event storms and stale queries), this interface
allows the WriteTool to refresh the graph synchronously BEFORE returning.

This prevents the "event storm loop" where:
1. Agent writes file
2. fsnotify fires, graph refresher starts parsing
3. Agent writes again (before refresh completes)
4. Agent queries graph - gets STALE version
5. Agent thinks fix didn't work, writes again → infinite loop

Thread Safety:

Implementations must be safe for concurrent use.

type GrepMatch

type GrepMatch struct {
	// File is the absolute path to the file containing the match.
	File string `json:"file"`

	// Line is the 1-indexed line number of the match.
	Line int `json:"line"`

	// Content is the matching line content.
	Content string `json:"content"`

	// ContextBefore contains lines before the match.
	ContextBefore []string `json:"context_before,omitempty"`

	// ContextAfter contains lines after the match.
	ContextAfter []string `json:"context_after,omitempty"`
}

GrepMatch represents a single match from a grep operation.

type GrepParams

type GrepParams struct {
	// Pattern is the regex pattern to search for.
	Pattern string `json:"pattern"`

	// Path is the file or directory to search in.
	Path string `json:"path,omitempty"`

	// Glob is an optional file pattern filter (e.g., "*.go").
	Glob string `json:"glob,omitempty"`

	// ContextLines is the number of lines to show before and after each match.
	ContextLines int `json:"context_lines,omitempty"`

	// CaseInsensitive enables case-insensitive matching.
	CaseInsensitive bool `json:"case_insensitive,omitempty"`

	// Limit is the maximum number of matches. Defaults to DefaultGrepLimit.
	Limit int `json:"limit,omitempty"`

	// Fuzzy enables fzf-style fuzzy matching where characters must appear
	// in order but not necessarily adjacent. Example: "prsfil" matches "parseFile".
	Fuzzy bool `json:"fuzzy,omitempty"`

	// Approximate enables agrep-style approximate matching using Levenshtein
	// distance. Example: "functon" matches "function" with MaxErrors=1.
	Approximate bool `json:"approximate,omitempty"`

	// MaxErrors is the maximum edit distance for approximate matching.
	// Only used when Approximate=true. Default: 2, Max: 5.
	MaxErrors int `json:"max_errors,omitempty"`
}

GrepParams defines parameters for the Grep tool.

func (*GrepParams) Validate

func (p *GrepParams) Validate() error

Validate checks that GrepParams are valid.

type GrepResult

type GrepResult struct {
	// Matches is the list of matching lines.
	Matches []GrepMatch `json:"matches"`

	// Count is the total number of matches found.
	Count int `json:"count"`

	// Truncated indicates if results were truncated due to limit.
	Truncated bool `json:"truncated"`

	// FilesSearched is the number of files searched.
	FilesSearched int `json:"files_searched"`

	// SearchPath is the directory or file that was searched.
	SearchPath string `json:"search_path"`
}

GrepResult contains the outcome of a Grep operation.

type GrepTool

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

GrepTool implements the Grep content search operation.

Thread Safety: GrepTool is safe for concurrent use.

func NewGrepTool

func NewGrepTool(config *Config) *GrepTool

NewGrepTool creates a new Grep tool with the given configuration.

func (*GrepTool) Category

func (t *GrepTool) Category() tools.ToolCategory

Category returns the tool category.

func (*GrepTool) Definition

func (t *GrepTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*GrepTool) Execute

func (t *GrepTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute searches for content matching the pattern.

func (*GrepTool) Name

func (t *GrepTool) Name() string

Name returns the tool name.

type JSONParams

type JSONParams struct {
	// FilePath is the JSON file to query/validate.
	FilePath string `json:"file_path"`

	// Query is a jq-style path (e.g., ".users[0].name").
	Query string `json:"query,omitempty"`

	// Validate only validates JSON, doesn't query.
	Validate bool `json:"validate,omitempty"`
}

JSONParams defines parameters for the JSON tool.

func (*JSONParams) ValidateParams

func (p *JSONParams) ValidateParams() error

JSONParamsValidate checks that JSONParams are valid. Note: Relative paths are allowed and should be resolved by the tool against the working directory.

type JSONResult

type JSONResult struct {
	// Value is the query result.
	Value any `json:"value,omitempty"`

	// Valid indicates if the JSON is valid.
	Valid bool `json:"valid"`

	// Error contains the parse error if invalid.
	Error string `json:"error,omitempty"`

	// Path is the file path.
	Path string `json:"path"`
}

JSONResult contains the outcome of a JSON operation.

type JSONTool

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

JSONTool implements JSON file querying and validation.

Thread Safety: JSONTool is safe for concurrent use.

func NewJSONTool

func NewJSONTool(config *Config) *JSONTool

NewJSONTool creates a new JSON tool with the given configuration.

func (*JSONTool) Category

func (t *JSONTool) Category() tools.ToolCategory

Category returns the tool category.

func (*JSONTool) Definition

func (t *JSONTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*JSONTool) Execute

func (t *JSONTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute queries or validates a JSON file.

func (*JSONTool) Name

func (t *JSONTool) Name() string

Name returns the tool name.

type LSPReleaser

type LSPReleaser interface {
	// ReleaseFile tells LSP servers to close the file.
	// Called before atomic writes on Windows.
	ReleaseFile(ctx context.Context, filePath string) error

	// ReopenFile tells LSP servers to reopen the file with new content.
	// Called after atomic writes on Windows.
	ReopenFile(ctx context.Context, filePath string, content string, languageID string) error
}

LSPReleaser provides methods to coordinate file access with LSP servers.

Description:

On Windows, LSP servers (like gopls) hold file handles open, which
prevents atomic file operations (os.Rename). This interface allows
the WriteTool to notify LSP servers to release file handles before
writing, and reopen them afterward.

Thread Safety:

Implementations must be safe for concurrent use.

type ReadParams

type ReadParams struct {
	// FilePath is the absolute path to the file to read.
	FilePath string `json:"file_path"`

	// Offset is the line number to start reading from (1-indexed).
	// If 0 or not provided, reads from the beginning.
	Offset int `json:"offset,omitempty"`

	// Limit is the maximum number of lines to read.
	// Defaults to DefaultReadLimit if not provided.
	Limit int `json:"limit,omitempty"`
}

ReadParams defines parameters for the Read tool.

func (*ReadParams) Validate

func (p *ReadParams) Validate() error

Validate checks that ReadParams are valid. Note: Relative paths are allowed and should be resolved by the tool against the working directory.

type ReadResult

type ReadResult struct {
	// Content is the file content with line numbers.
	Content string `json:"content"`

	// TotalLines is the total number of lines in the file.
	TotalLines int `json:"total_lines"`

	// LinesRead is the number of lines actually read.
	LinesRead int `json:"lines_read"`

	// Truncated indicates if output was truncated due to limits.
	Truncated bool `json:"truncated"`

	// TruncatedAt is the line number where truncation occurred.
	TruncatedAt int `json:"truncated_at,omitempty"`

	// BytesRead is the number of bytes read from the file.
	BytesRead int64 `json:"bytes_read"`

	// FileType indicates the detected file type.
	FileType string `json:"file_type,omitempty"`
}

ReadResult contains the outcome of a Read operation.

type ReadTool

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

ReadTool implements the Read file operation.

Thread Safety: ReadTool is safe for concurrent use.

func NewReadTool

func NewReadTool(config *Config) *ReadTool

NewReadTool creates a new Read tool with the given configuration.

func (*ReadTool) Category

func (t *ReadTool) Category() tools.ToolCategory

Category returns the tool category.

func (*ReadTool) Definition

func (t *ReadTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*ReadTool) Execute

func (t *ReadTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute reads a file and returns its contents with line numbers.

func (*ReadTool) Name

func (t *ReadTool) Name() string

Name returns the tool name.

type TreeParams

type TreeParams struct {
	// Path is the directory to visualize.
	Path string `json:"path,omitempty"`

	// Depth is the maximum depth to traverse (default: 3, max: 10).
	Depth int `json:"depth,omitempty"`

	// ShowHidden includes dotfiles/dotdirs.
	ShowHidden bool `json:"show_hidden,omitempty"`

	// DirsOnly shows only directories, not files.
	DirsOnly bool `json:"dirs_only,omitempty"`
}

TreeParams defines parameters for the Tree tool.

func (*TreeParams) Validate

func (p *TreeParams) Validate() error

Validate checks that TreeParams are valid.

type TreeResult

type TreeResult struct {
	// Tree is the ASCII tree output.
	Tree string `json:"tree"`

	// TotalDirs is the number of directories found.
	TotalDirs int `json:"total_dirs"`

	// TotalFiles is the number of files found.
	TotalFiles int `json:"total_files"`

	// Truncated indicates if depth limit was hit.
	Truncated bool `json:"truncated"`
}

TreeResult contains the outcome of a Tree operation.

type TreeTool

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

TreeTool implements the Tree directory visualization operation.

Thread Safety: TreeTool is safe for concurrent use.

func NewTreeTool

func NewTreeTool(config *Config) *TreeTool

NewTreeTool creates a new Tree tool with the given configuration.

func (*TreeTool) Category

func (t *TreeTool) Category() tools.ToolCategory

Category returns the tool category.

func (*TreeTool) Definition

func (t *TreeTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*TreeTool) Execute

func (t *TreeTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute generates a tree visualization of a directory.

func (*TreeTool) Name

func (t *TreeTool) Name() string

Name returns the tool name.

type WriteParams

type WriteParams struct {
	// FilePath is the absolute path for the new file.
	FilePath string `json:"file_path"`

	// Content is the file content to write.
	Content string `json:"content"`
}

WriteParams defines parameters for the Write tool.

func (*WriteParams) Validate

func (p *WriteParams) Validate() error

Validate checks that WriteParams are valid. Note: Relative paths are allowed and should be resolved by the tool against the working directory.

type WriteResult

type WriteResult struct {
	// Success indicates if the write succeeded.
	Success bool `json:"success"`

	// BytesWritten is the number of bytes written.
	BytesWritten int64 `json:"bytes_written"`

	// Path is the absolute path of the written file.
	Path string `json:"path"`

	// Created indicates if a new file was created (vs overwritten).
	Created bool `json:"created"`
}

WriteResult contains the outcome of a Write operation.

type WriteTool

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

WriteTool implements the Write file operation.

Thread Safety: WriteTool is safe for concurrent use.

func NewWriteTool

func NewWriteTool(config *Config) *WriteTool

NewWriteTool creates a new Write tool with the given configuration.

func (*WriteTool) Category

func (t *WriteTool) Category() tools.ToolCategory

Category returns the tool category.

func (*WriteTool) Definition

func (t *WriteTool) Definition() tools.ToolDefinition

Definition returns the tool's parameter schema.

func (*WriteTool) Execute

func (t *WriteTool) Execute(ctx context.Context, params tools.TypedParams) (*tools.Result, error)

Execute writes content to a file using atomic write.

func (*WriteTool) Name

func (t *WriteTool) Name() string

Name returns the tool name.

Jump to

Keyboard shortcuts

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