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
- func Apply(command, raw string, exit int) (filtered string, saved int)
- func BaseCommand(command string) []string
- func CapLines(lines []string) []string
- func DedupConsecutive(lines []string) []string
- func IsTrivial(command string) bool
- func JoinLines(lines []string, endedNL bool) string
- func LedgerKey(command string) string
- func SplitLines(s string) []string
- func SqueezeBlanks(lines []string) []string
- func StripANSI(s string) string
- func TrimBlankEdges(lines []string) []string
- func TrimTrailingSpace(lines []string) []string
- type Filter
- type GitFilter
- type GoFilter
- type InstallFilter
- type KubectlFilter
- type OutputMatch
- type PytestFilter
- type Replacement
- type Rule
Constants ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
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 ¶
JoinLines rejoins lines with newlines, restoring a trailing newline when the original output had one.
func LedgerKey ¶ added in v1.2.0
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 ¶
SplitLines splits on newlines without allocating a final empty element for a trailing newline — callers re-add the terminator via JoinLines.
func SqueezeBlanks ¶
SqueezeBlanks collapses runs of blank lines into a single blank line.
func StripANSI ¶
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 ¶
TrimBlankEdges drops leading and trailing blank lines.
func TrimTrailingSpace ¶
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.
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.
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 ¶
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 ¶
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.