watch

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package watch turns in-tree source comments into governed run inputs. It tails a working tree for aider-style ai! and ai? markers left in trailing comments; when one appears it becomes a turn the agent runs, and the marker's file and line are carried onto the run so its provenance lands in the sealed record like any other input. The marker is cleared once picked up so the same request never fires twice.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Clear

func Clear(content []byte, line int) ([]byte, bool)

Clear removes the marker on the given 1-based line from content and reports whether it changed anything. The marker's comment is stripped from its line; if nothing but whitespace remains the line is dropped entirely, otherwise the code before the comment is kept. A line that no longer holds a marker (already cleared, edited out) leaves content untouched and returns false, so a lost race clears nothing rather than corrupting the file. The file's newline style is preserved: a trailing carriage return on the target line survives when the code is kept.

Types

type Handler

type Handler func(Marker) error

Handler runs one detected marker. Returning nil means the marker was picked up and the watcher clears it from its file; a non-nil error leaves the file untouched and the marker is not retried until it is removed and re-added.

type Ignore

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

Ignore matches repo-relative paths against a set of .gitignore rules so the watch walk skips the files git would. It covers the common cases: blank and # comment lines, ! negation, a trailing / for directory-only rules, a leading / to anchor to the root, * globs within a path segment, and ** to span segments. Last matching rule wins, matching git's precedence. It is not the full gitignore grammar (no character classes, no rooted-vs-nested subtleties beyond anchoring); it errs toward matching the everyday patterns a working tree actually carries.

func ParseIgnore

func ParseIgnore(content []byte) *Ignore

ParseIgnore builds an Ignore from .gitignore file contents. Paths are always matched with forward slashes regardless of the host separator.

func (*Ignore) Match

func (ig *Ignore) Match(rel string, isDir bool) bool

Match reports whether the repo-relative path (slash-separated) is ignored. isDir lets a directory-only rule apply only to directories; a walk prunes an ignored directory, so its descendants are never visited.

type Kind

type Kind string

Kind is a marker's intent. Act (ai!) asks the agent to change the code; Ask (ai?) asks it to answer about the code. Both become a turn input; the kind is carried so the composed objective can tell the run to edit versus explain.

const (
	// Act is the ai! marker: do the work described in the comment.
	Act Kind = "ai!"
	// Ask is the ai? marker: answer the question the comment poses.
	Ask Kind = "ai?"
)

func ScanLine

func ScanLine(line string) (kind Kind, instruction, code string, ok bool)

ScanLine reports the marker on a single line, if any. It finds the earliest comment leader whose following token is ai! or ai?, takes the rest of the comment as the instruction (trailing block terminators removed), and returns the code before the comment as context. ok is false when the line holds no marker or the instruction is empty (an empty marker has nothing to run).

type Marker

type Marker struct {
	Kind Kind
	File string
	Line int
	Text string
	Code string
}

Marker is one ai! / ai? request found in a source file: the intent, the file and 1-based line it sits on, the instruction text after the marker token, and the code on that line with the marker comment removed (the local context the request is about). Provenance renders File and Line for the recorded objective.

func Scan

func Scan(file string, content []byte) []Marker

Scan finds every marker in a file's content, tagging each with the file path and its 1-based line. Lines without a marker are skipped.

func (Marker) Provenance

func (m Marker) Provenance() string

Provenance renders where a marker came from for the recorded objective, e.g. "cmd/flynn/run.go:42 (ai!)". It is stable so a run's inputs are diffable.

type Watcher

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

Watcher polls a working tree for ai! / ai? markers and feeds each new one to a Handler exactly once, clearing it from its file after a successful pickup. It reads time through a clock.Timing so a Manual clock drives it deterministically in tests, mirroring the terminal-resize watcher. Markers are processed one at a time in the polling goroutine, so a marker never starts a run while an earlier one is still running.

func Start

func Start(timing clock.Timing, root string, interval time.Duration, handle Handler) *Watcher

Start begins watching root, polling every interval and invoking handle for each new marker. It loads a root-level .gitignore if present and always skips .git. The returned Watcher runs until Stop is called.

func (*Watcher) Stop

func (w *Watcher) Stop()

Stop ends the watch and waits for the polling goroutine to exit, so no handler can fire after Stop returns.

Jump to

Keyboard shortcuts

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