presenters

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package presenters renders structured query results as text for CLI output. Presenters take pure data from finders plus an io.Writer and formatting parameters; they have no IO of their own beyond the writer.

The package isolates the view layer from finders (data) and from CLI command plumbing — separating concerns so each can be tested in isolation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EntryLine

func EntryLine(w io.Writer, e *model.Entry, g *model.Graph)

EntryLine writes a single entry summary line — used by status, list, and other surfaces that show entries in a flat list.

Format: `<id> <layer> <kind>? <type> [confidence: <conf>]? (<participants>) {status: <status>}? {score: <score>}? <topics>? <summary>` Kind renders as a qualifier alongside layer/type — it's identity, not an attribute (d-cpt-omm's two-type redesign makes every entry carry a kind). Square brackets denote stored attributes (today: confidence); curly braces denote derived attributes computed from graph relationships (d-tac-3yi); angle brackets denote topic membership (also derived — inline topics merged with annotation declarations). Participants are always present — empty is rendered as `()`. Status is present for signals and decisions; omitted for done signals (terminal). Topics are omitted entirely when the effective set is empty. Score is per-rendering, computed by a rank algorithm; emitted only via EntryLineWithScore (slice 3 — d-tac-uww).

func EntryLineWithScore added in v0.5.0

func EntryLineWithScore(w io.Writer, e *model.Entry, g *model.Graph, score float64)

EntryLineWithScore is EntryLine plus a `{score: X.XXX}` segment after status. Used by the as-list renderer when the section was ranked.

func FormatConfidence

func FormatConfidence(c string) string

FormatConfidence renders a stored confidence attribute in square-bracket notation.

func FormatScore added in v0.5.0

func FormatScore(score float64) string

FormatScore renders a per-rendering rank score in curly-brace notation alongside the other derived-attribute segments. Three decimal places balance precision with line length — heat scores are typically small floats (a few units) and three places preserves enough resolution to distinguish similar entries.

func FormatSearchCapability added in v0.4.0

func FormatSearchCapability(vectorAvailable bool) string

FormatSearchCapability renders the capability suffix shown in `sdd status`'s header: `text` when only text mode is available; `vector,text` when a vector embedder is configured. Used by callers that want to surface the search surface alongside other status fields.

func FormatStatus

func FormatStatus(s model.Status) string

FormatStatus renders a derived status in curly-brace notation. Returns the empty string for StatusNone so callers can omit the attribute. Compound states use a space separator in the value (`closed-by <id>`) to avoid ambiguity with the outer `{key: value}` delimiter.

func FormatStatusTrail added in v0.9.0

func FormatStatusTrail(s model.Status, supersedePath []string) string

FormatStatusTrail renders the status segment for a ref sub-line, expanding a superseded target into its full supersede trail through to the live head: `{status: superseded-by <hop1> → … → <head>}`. supersedePath is the resolved origin→head path (model.ResolveRef(...).Path()); its first element is the origin (the sub-line's own entry) and is dropped, so the rendered hops are the superseders ending at the head. When the target is not superseded (path length ≤ 1) this is identical to FormatStatus — only ref sub-lines, where a reader is traversing a reference, surface the intermediate hops; flat surfaces keep the head-only form.

func FormatTopics added in v0.5.0

func FormatTopics(paths []model.TopicPath) string

FormatTopics renders an entry's effective topic set in angle-bracket notation (`<label1, label2>`). Returns the empty string when the set is empty so callers can omit the segment entirely.

func GroupByLayer

func GroupByLayer(entries []*model.Entry) map[model.Layer][]*model.Entry

GroupByLayer groups entries by layer for display. Returns a map from layer to entries; iterate with LayerOrder to render in canonical order.

func LayerOrder

func LayerOrder() []model.Layer

LayerOrder returns the display order for layers (strategic → process).

func RenderInfo added in v0.5.2

func RenderInfo(w io.Writer, result *query.InfoResult)

RenderInfo writes the session-framing header — participant, language (when configured), and search capability. The same lines prefix `sdd status` (via writeInfoHeader), so the two surfaces stay in lockstep.

func RenderInitSkills added in v0.2.0

func RenderInitSkills(w io.Writer, installDir string, result command.SkillInstallResult)

RenderInitSkills summarises a skill install pass for the user. Counts are always printed; individual paths appear only for categories that warrant per-path attention (overwritten, skipped-modified).

func RenderLint

func RenderLint(w io.Writer, result *query.LintResult, g *model.Graph)

RenderLint writes a human-readable lint report to w. Returns nothing — the caller decides what to do about a non-zero issue count (typically returning a non-zero exit code from the CLI).

func RenderList

func RenderList(w io.Writer, result *query.ListResult)

RenderList writes one EntryLine per matched entry.

func RenderResultLine added in v0.9.0

func RenderResultLine(dst io.Writer, headline, detail string)

RenderResultLine writes a completed-operation summary in the CLI palette: a prominent headline (bold white, the qualifier style) followed by faint detail. On a non-TTY destination or with NO_COLOR the colorprofile writer downsamples to plain text, so agents and pipes get a clean line. detail may be empty.

This is the styled-feedback surface the terminal-experience directive (d-cpt-mvb) calls for, sharing the palette established by d-cpt-n0f so the summary reads as one piece with sdd show and sdd stats.

func RenderSchemaError added in v0.2.0

func RenderSchemaError(w io.Writer, compat model.CompatibilityResult)

RenderSchemaError writes a user-facing explanation of a compatibility failure to w. The CompatibilityResult.Reason is already human-readable; this presenter frames it with a fixed prefix and exit hint so output reads the same across every write command.

func RenderSearch added in v0.4.0

func RenderSearch(w io.Writer, result *query.SearchResult, g *model.Graph)

RenderSearch writes one section per ranked entry: a header line that matches `sdd list` shape, then one citation line per entry-citation (capped by SearchQuery.MaxCitationsPerEntry on the finder side).

Render format (single-citation case):

<id> <layer> <kind>? <type> [confidence: ?]? (<participants>) {status: ?}? <summary>
  ↳ 100%  ·  Breadcrumb > Chain  ·  <snippet>

Render format (multi-citation case):

<id> ...
  ↳ 100%  ·  Summary  ·  <snippet>
  ↳  91%  ·  Approach > Storage  ·  <snippet>
  ↳  87%  ·  ... [attachment: ...]  ·  <snippet>

The percentage is each citation's score normalized against the strongest score in the result set — the top citation everywhere renders 100%, others scale relative. This is honest about the cross- entry ranking without making any claims about absolute "confidence" (cosine values aren't calibrated across embedders, and text-mode match counts and hybrid RRF scores live on different scales than cosine — but ALL citations within a single result set share whatever scale the active mode uses).

func RenderShow

func RenderShow(w io.Writer, result *query.ShowResult, opts ShowOptions)

RenderShow writes the plain-markdown show output for a ShowResult. Each group renders as a YAML frontmatter envelope + raw markdown body for the primary, followed by compact markdown trees for the upstream and downstream neighborhoods (each present to the depth the query set; an empty direction is omitted). Groups are separated by a blank line — each is a self-contained frontmatter+body markdown document.

This is the agent / pipe / `--format text` renderer; the styled terminal renderer shares the same data model (see the styled renderer / slice 3).

func RenderShowStyled added in v0.9.0

func RenderShowStyled(dst io.Writer, result *query.ShowResult, opts ShowOptions)

RenderShowStyled writes the styled, human-facing show output for an interactive terminal: a dimmed YAML envelope, the markdown body rendered with glamour, and a lipgloss-styled neighborhood tree (relation-kind color, dimmed status, dim indent guides). It shares the read-side data model with the plain renderer (RenderShow) — only presentation differs.

The colorprofile writer downsamples ANSI to the destination's capability and strips it for a non-terminal writer (d-cpt-mvb), so a plain io.Writer — a test buffer or a pipe — receives clean, color-free structured text.

func RenderStatsJSON added in v0.9.0

func RenderStatsJSON(w io.Writer, r *query.StatsResult) error

RenderStatsJSON writes the same aggregates as structured JSON on the agent / non-TTY path — no styling, no chrome. An empty sink yields valid JSON with zeroed totals and empty arrays (a clean result, exit 0), not an error.

func RenderStatsTable added in v0.9.0

func RenderStatsTable(dst io.Writer, r *query.StatsResult)

RenderStatsTable writes the styled, human-facing usage report: a header and source line, an overall totals block, then per-model and per-op tables. Used on the interactive TTY path. The empty cases (no sink yet, or nothing in the selected range) render a single explanatory line instead of blank tables.

lipgloss styles always emit ANSI at Render() time; the colorprofile writer downsamples to the destination's actual capability and strips color when NO_COLOR is set or the writer is not a terminal — so a plain io.Writer (a test buffer, a pipe) gets clean text and a real TTY keeps color (d-cpt-mvb).

func RenderStatus

func RenderStatus(w io.Writer, result *query.StatusResult)

RenderStatus writes the status view: top-line counts, then one section per decision kind (Aspirations, Contracts, Plans, Activities, Directives), followed by the signal sections (Gaps and Questions, Recent Insights, Recent Done Signals). Decision-kind sections are grouped by layer; signal sections are flat activity streams. Empty sections are suppressed — the section header implies membership, so "open" / "active" prefixes are not needed on the headers.

func RenderView added in v0.5.0

func RenderView(w io.Writer, result *query.ViewResult)

RenderView writes one section per SectionResult, dispatching to the per-render helper based on the section's Render name. Sections are separated by a blank line so visual clusters are obvious. A non-empty Section.Name renders as a `## <name>` header before the section body; empty Name omits the header (the as-list / as-grouped default).

Render names are matched against the slice's known set; unknown names silently produce no output for that section. The finder validates the pipeline shape before reaching here, so an unknown name implies a programmer-side mistake (a layout produced by some path other than the parser+executor) rather than a user-facing error.

func RenderWIPList

func RenderWIPList(w io.Writer, result *query.WIPListResult)

RenderWIPList writes the active WIP markers in a fixed-width layout.

Types

type ShowOptions added in v0.9.0

type ShowOptions struct {
	// WithSummary includes the primary's stored summary in the envelope.
	// Off by default — the body renders right after the envelope, and the
	// summary is only needed for human drift-review.
	WithSummary bool
	// Width is the target wrap width for the styled renderer's glamour body
	// (terminal columns). Zero falls back to a sensible default. Ignored by
	// the plain renderer, which never reflows the body.
	Width int
}

ShowOptions controls optional segments of the show rendering.

Jump to

Keyboard shortcuts

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