filters

package
v1.11.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package filters compresses shell-command output before it reaches the model, RTK-style: an ordered set of command-aware semantic filters over a universal fallback. Every filter is pure (no I/O, no shared state), so the whole pipeline is safe to run concurrently from each bash call.

Index

Constants

View Source
const MaxLines = 1000

MaxLines caps universal (post-filter) output middle-out. Semantic filters do the heavy compression; this is only the safety net for commands with no dedicated filter, so it stays generous — the point is to bound pathological output, not to drop signal.

Variables

This section is empty.

Functions

func Apply

func Apply(command, raw string, exit int) (filtered string, saved int)

Apply routes raw output through the matching semantic filter (if any), then the universal fallback pipeline: trailing-space trim, blank squeeze, edge trim, middle-out cap. Commands with no dedicated filter additionally get consecutive-run dedup — a semantic filter's output is left un-deduped because those formats carry meaningful repeats. saved is estimated at bytes/4 against the untouched input, matching the ledger's convention elsewhere.

func BaseCommand

func BaseCommand(command string) []string

BaseCommand identifies the primary command of a possibly-chained shell line and returns its fields (program plus arguments, with wrapper, keyword, and env-assignment noise stripped). It is the single source of truth for command identification: the filter registry dispatches on it and the savings ledger keys on it (via LedgerKey), so the two always agree.

Selection across pipeline/statement stages: the last stage a registered filter claims wins (so `go test ./... | tail` is a go-test command, and `cmake .. && make` is a make command); failing that, the last stage whose program is not a pager/transformer (so `kubectl get pods | head` is a kubectl command); failing that, the last stage.

func CapLines

func CapLines(lines []string) []string

CapLines truncates middle-out to MaxLines, leaving a marker naming how many lines were dropped. Head and tail are kept because that is where a command's setup and verdict live.

func DedupConsecutive

func DedupConsecutive(lines []string) []string

DedupConsecutive collapses runs of identical lines into one line tagged with a `(×N)` multiplier. Only applied to the universal fallback — semantic-filter output carries meaningful repeats and is left alone.

func IsTrivial added in v1.2.0

func IsTrivial(command string) bool

IsTrivial reports whether a command is not a meaningful savings opportunity: its primary program is a pager, text transformer, or a tiny builtin (cat, grep, head, cd, echo, …), or there is no command at all. The ledger skips these so it reflects real build/test/tooling commands rather than file reads and shell plumbing.

func JoinLines

func JoinLines(lines []string, endedNL bool) string

JoinLines rejoins lines with newlines, restoring a trailing newline when the original output had one.

func LedgerKey added in v1.2.0

func LedgerKey(command string) string

LedgerKey is the savings-ledger key for a command: the primary program (directory prefix stripped), plus its subcommand for subcommand-style tools. Empty when there is no command.

func SplitLines

func SplitLines(s string) []string

SplitLines splits on newlines without allocating a final empty element for a trailing newline — callers re-add the terminator via JoinLines.

func SqueezeBlanks

func SqueezeBlanks(lines []string) []string

SqueezeBlanks collapses runs of blank lines into a single blank line.

func StripANSI

func StripANSI(s string) string

StripANSI removes CSI escape sequences (colors, cursor moves). It is the first thing every filter sees, so downstream matching is on plain text.

func TrimBlankEdges

func TrimBlankEdges(lines []string) []string

TrimBlankEdges drops leading and trailing blank lines.

func TrimTrailingSpace

func TrimTrailingSpace(lines []string) []string

TrimTrailingSpace strips trailing spaces/tabs/CR from every line.

Types

type Filter

type Filter interface {
	// Name identifies the filter in diagnostics; it is also the shape of
	// the savings-ledger key for commands this filter claims.
	Name() string
	// Match reports whether this filter handles the command whose parsed
	// fields these are — env assignments already stripped, fields[0] the
	// program (path base not yet stripped; use the progOf helper).
	Match(fields []string) bool
	// Filter rewrites raw (ANSI already stripped) into compact form. exit
	// is the process exit code and steers verbosity: 0 may collapse hard to
	// a summary; nonzero must preserve failure detail; an unmodeled nonzero
	// should return raw untouched so a real failure is never hidden.
	Filter(fields []string, raw string, exit int) string
}

Filter is a command-aware output compressor.

type GitFilter

type GitFilter struct{}

GitFilter compresses git's human output into compact one-line-per-item form: status becomes porcelain-style letters under a branch header, log collapses to short-hash + subject, a large diff/show becomes per-file insertion/deletion stats, and transport commands drop remote chatter. Any output that does not match the expected shape is returned unchanged — a filter must never destroy output it does not understand.

func (GitFilter) Filter

func (GitFilter) Filter(fields []string, raw string, exit int) string

func (GitFilter) Match

func (GitFilter) Match(fields []string) bool

func (GitFilter) Name

func (GitFilter) Name() string

type GoFilter

type GoFilter struct{}

GoFilter handles the `go` toolchain. `go test` gets the full treatment: a `-json` event stream is aggregated into a per-package pass/fail/skip summary with failing-test output kept verbatim, and human text mode drops the RUN/PASS chatter while keeping ok/FAIL lines. It handles only `go test`; the other `go` subcommands are declarative rules (rules.go: go build/vet → pass/fail, go mod/get → download strip, go run → passthrough), so subcommand behavior lives in one place.

func (GoFilter) Filter

func (GoFilter) Filter(fields []string, raw string, exit int) string

func (GoFilter) Match

func (GoFilter) Match(fields []string) bool

func (GoFilter) Name

func (GoFilter) Name() string

type InstallFilter

type InstallFilter struct{}

InstallFilter strips the noise from JavaScript package-manager installs (npm, yarn, pnpm, bun): deprecation warnings, funding/audit footers, and progress lines. The final "added N packages" style result line is kept. A failed install keeps everything, since the error text is what matters.

func (InstallFilter) Filter

func (InstallFilter) Filter(fields []string, raw string, exit int) string

func (InstallFilter) Match

func (InstallFilter) Match(fields []string) bool

func (InstallFilter) Name

func (InstallFilter) Name() string

type KubectlFilter added in v1.2.0

type KubectlFilter struct{}

KubectlFilter compresses kubectl output. Its biggest win is dropping the `managedFields` block from `-o yaml` / `-o json` resource dumps — pure server-side-apply bookkeeping that is often larger than the resource itself and never useful to a reader. It also strips kubectl's own deprecation and version-skew warnings. Everything else (get tables, describe, logs) passes through, and a failed command is returned verbatim so the error is never hidden.

func (KubectlFilter) Filter added in v1.2.0

func (KubectlFilter) Filter(fields []string, raw string, exit int) string

func (KubectlFilter) Match added in v1.2.0

func (KubectlFilter) Match(fields []string) bool

func (KubectlFilter) Name added in v1.2.0

func (KubectlFilter) Name() string

type OutputMatch

type OutputMatch struct {
	Pattern *regexp.Regexp
	Message string
}

OutputMatch is a whole-output success detector.

type PytestFilter

type PytestFilter struct{}

PytestFilter compresses pytest runs. On a clean run it collapses to the final summary line ("=== 42 passed in 1.2s ==="), dropping the platform banner and the per-file progress dots. On a failing run it keeps the FAILURES/ERRORS section onward — the tracebacks, the short test summary, and the final line — while dropping the noisy preamble. Anything it does not recognize is returned untouched.

func (PytestFilter) Filter

func (PytestFilter) Filter(fields []string, raw string, exit int) string

func (PytestFilter) Match

func (PytestFilter) Match(fields []string) bool

func (PytestFilter) Name

func (PytestFilter) Name() string

type Replacement

type Replacement struct {
	Pattern *regexp.Regexp
	Repl    string
}

Replacement is one regex substitution.

type Rule

type Rule struct {
	// Name is the diagnostics / ledger-key label.
	Name string
	// Command matches the normalized command (program base + args joined by
	// single spaces, env assignments stripped), e.g. `^make\b`.
	Command *regexp.Regexp
	// Strip drops any line matching one of these.
	Strip []*regexp.Regexp
	// Keep, when non-empty, keeps only lines matching one of these (applied
	// after Strip).
	Keep []*regexp.Regexp
	// Replace applies regex substitutions before line filtering.
	Replace []Replacement
	// PassFail turns the command into a binary result: on success (exit 0)
	// the whole output collapses to a single "ok" line (OnEmpty if set, else
	// "<name>: ok") regardless of content; on failure it falls through to the
	// normal strip/keep pipeline (or raw, with KeepOnError) so the error is
	// still shown. For commands whose successful output is pure noise
	// (builds, formatters) and only the exit code matters.
	PassFail bool
	// Summarize collapses the whole output to Message when Pattern matches
	// AND the command succeeded (exit 0). This is the success-detection
	// short-circuit ("BUILD SUCCESSFUL" → "gradle: ok"); it never fires on a
	// failing run, so an error is never replaced by a rosy summary.
	Summarize []OutputMatch
	// MaxLines keeps the first N lines (0 = unbounded); TailLines keeps the
	// last N. Prefer leaving these zero on build/test commands so a late
	// error is never truncated — the universal cap still bounds runaways.
	MaxLines  int
	TailLines int
	// TruncateAt caps each line's length in bytes (0 = unbounded).
	TruncateAt int
	// OnEmpty is emitted when filtering removed everything ("make: ok").
	OnEmpty string
	// KeepOnError returns the raw output verbatim on a nonzero exit.
	KeepOnError bool
}

Rule is a declarative, data-driven filter — ask's equivalent of one of RTK's src/filters/*.toml files. A table of Rules (see rules.go) covers the long tail of commands without a bespoke Go type each; complex aggregators (go test, git, pytest) stay hand-written. Every Rule is registered as its own Filter through ruleFilter, so ordering and precedence work exactly like the hand-written filters.

Jump to

Keyboard shortcuts

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