A single Go binary that keeps a developer's view of a repository current in a terminal pane: a
live-updating git log, and a deterministic recap of the project's GSD planning state
(.planning/) — wave-by-wave status, plan details, human checkpoints — rendered from the
artifacts without an AI call.
It reads and never writes. No command below modifies the repository, .planning/, or any file
the tool opens. Redrawing is event-driven: the tool never polls, and an idle pane costs
essentially nothing.
Prebuilt binaries for macOS, Linux and Windows (amd64 and arm64) are attached to each release.
With a Go toolchain (1.25.8 or newer):
go install github.com/electroheadfx/efx-next/cmd/efx-next@latestRequires Go 1.25.8 or newer (glamour v2's own requirement; 1.27.1 is recommended).
go build ./cmd/efx-next # produces ./efx-nextThen, from anywhere inside a git repository:
./efx-next # the interactive view
./efx-next log # the log, once, as text (--json for the machine contract)
./efx-next recap # the planning recap, once, as textThe interactive view is keyboard-driven — Tab switches between the log and the recap, ?
opens the key overlay, q quits. Mouse capture is deliberately left off, so the terminal's own
select-and-copy keeps working. --color auto|always|never overrides the colour decision.
Optional. If the repository root carries a file named .efx-next.yaml, it can declare the two
commands the tool will run for you on request.
The file lives at the repository root rather than in .planning/ because it configures this
tool, and .planning/ holds the project's GSD artifacts, which the tool never writes and only
reads.
build: [go, build, ./...]
test: [go, test, ./...]
timeout: 2m
error_pattern: '(?i)^(FAIL|Error):'
exclude: ["vendor/", "*.pb.go"]Commands are argument lists, not shell strings. build: [go, build, ./...] runs go with
the arguments build and ./.... There is no shell: build: go build ./... is refused rather
than passed to /bin/sh, because a shell would reinterpret what you wrote — pipes, globs,
substitutions, quoting — and this tool runs your command, not a program that reads it. Each
argument is its own list element, verbatim.
timeout bounds one run. It takes a Go duration (2m, 90s, 1h30m); a bare number is
not seconds. The default is 120s, which is generous on purpose — a run stopped too early is a
false failure, while a run that never ends is a process you have to hunt down. A run stopped by
its timeout is reported as stopped, never as a clean one.
error_pattern replaces the built-in rule that decides which output lines count as
errors. Without it, the tool's own generic rule does the counting; with it, your regular
expression does, and the measured line says which rule produced the number.
exclude leaves paths out of the lines-of-code figure. A trailing / names a directory and
matches it at any depth. An entry whose last component begins with * names files whose name
ends with the text after the *; any other entry names a file by its own name, and anything
before that name is the directory it must sit under. What was left out is named on the figure's
own line.
When the file is absent, none of this exists: no build or test key, no entry in the key overlay, no line in the recap. The feature is absent rather than present-and-disabled. When the file is present but unusable, the problem is stated rather than silently ignored — you asked for the feature by writing the file, so a file the loader refused is a fact worth telling you.
Pressing the build or test key runs the command that file declares, as written, in your repository, with your privileges. Nothing else is ever executed: no command is run at startup, on a timer, or on a file event, and no shell is involved. The file is trusted input — treat it the way you would treat a shell script you keep in the repository.
The key overlay says the same thing where you need it: with a configuration loaded, the recap's
overlay closes with commands run as written, with your privileges.
MIT — see LICENSE.