Documentation
¶
Overview ¶
Package filerefs implements the @file reference system: when a user types something like "explain @main.go and @internal/foo.go", the agent detects the @-prefixed tokens, reads the matching files from the working directory, and injects their contents into the system prompt so the model sees the file as authoritative context for the turn — no extra read_file tool call required.
The package is split into three small phases:
- Parse extracts @<path> tokens from a user message.
- Load reads each token's file relative to a cwd, with size caps and a strict cwd-confinement check (no symlinks, no traversal).
- Inject rewrites a system-prompt string by stripping any prior auto-injected section and appending a fresh one for the current turn's refs.
All file-load failures are recorded on the Ref itself (Error field) rather than returned globally — the caller renders them as warnings and still sends the turn. Hard-failing on a missing @file would be frustrating in practice (typos, deleted files, paths inside compound words like "@TODO").
Index ¶
Constants ¶
const ( MaxFileSize = 200 * 1024 // 200 KiB per file MaxTotalSize = 1024 * 1024 // 1 MiB total per turn )
Size caps. Per-file is enforced first; total accumulates as files load and short-circuits the rest once breached. Both are deliberate (small) so the system prompt never balloons.
const Marker = "## Referenced files (auto-injected from @-prefixed paths)"
Marker is the heading that identifies an auto-injected file-refs block in a system prompt. Inject strips any prior block matching this marker before appending a new one — which keeps the prompt from accumulating stale snapshots across turns.
Variables ¶
This section is empty.
Functions ¶
func BuildSection ¶
BuildSection formats a slice of Refs into a markdown block ready for injection into the system prompt. Refs whose Loaded is false are emitted as inline error notices so the model sees the failure instead of silently missing a reference. Returns "" when refs is empty so callers can short-circuit "no refs → no injection."
func Inject ¶
Inject rewrites prompt by stripping any prior auto-injected block and appending a fresh one for the supplied refs. When refs is empty the prompt is returned with the block stripped (so a turn that uses no refs cleans up after one that did).
func Rewrite ¶
Rewrite returns input with the leading `@` stripped from every successfully-loaded ref's token. Refs that failed to load keep their `@` so the model can ask the user about the typo / missing path explicitly.
Why this exists: without rewriting, the user's message still contains `@docs/foo.md` after we've injected the file's contents into the system prompt. Some models then call `read_file("@docs/foo.md")` — passing the literal `@` to the tool, which fails because the `@` becomes part of the path. Stripping it produces a plain path that pairs naturally with the section we already injected up in the system prompt and removes the failure mode entirely.
func StripSection ¶
StripSection removes a prior auto-injected file-refs block from a system prompt, returning the prompt with the section excised. Used by Inject so successive turns replace (not accumulate) the block. If no marker is present, prompt is returned unchanged.
Types ¶
type Ref ¶
type Ref struct {
Token string // the original "@path" as it appeared in input
Path string // the parsed path (without the leading @)
Loaded bool
Content string
Size int // bytes read (post-truncation)
Error string // populated when Loaded is false
IsDir bool // true when Path resolves to a directory
}
Ref describes a single @-prefixed reference detected in user input. After Load, exactly one of Loaded or Error is meaningful: Loaded=true + Content populated, or Loaded=false + Error explaining why.
func Load ¶
Load reads each ref's file relative to cwd and fills Content / Error in place. Refs are returned in the same order. The cwd is canonicalized once so symlink/traversal checks compare against a stable root; absolute paths and any path that resolves outside cwd are rejected with a clear Error rather than read.
Size enforcement: a file larger than MaxFileSize is read up to the cap and marked with a "...truncated" note in Content. Once the running total exceeds MaxTotalSize, every remaining ref is rejected with a budget-exhausted Error so the system prompt stays bounded.