renderer

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: MIT Imports: 8 Imported by: 0

README

Renderer

internal/renderer owns all presentation logic for manly command output.

It converts typed presentation views into one of five output formats:

compact   Minimal terminal output; default
fancy     Rich terminal output
json      Structured machine-readable output
markdown  Markdown output
agent     Compact catalog metadata for agent retrieval

The package deliberately does not import internal/knowledge. It does not load bundles, search concepts, resolve links, or parse CLI arguments.

Architecture

cmd/manly
   |
   |  query and transform knowledge data
   v
renderer.View
   |
   |  renderer.New(format)
   v
renderer.Render(writer, view)
   |
   +--> compact
   +--> fancy
   +--> json
   +--> markdown
   +--> agent (list metadata only)

The dependency direction is:

cmd/manly -> internal/knowledge
cmd/manly -> internal/renderer
internal/renderer -> Go standard library

Public API

Formats

Format identifies an output format:

format, err := renderer.ParseFormat(value)
if err != nil {
    return err
}

Supported values are defined by:

renderer.FormatCompact
renderer.FormatFancy
renderer.FormatJSON
renderer.FormatMarkdown
renderer.FormatAgent

Invalid values produce an error containing all available formats:

unsupported format "human"; available formats: compact, fancy, json, markdown, agent
Renderer interface

Every format renderer implements:

type Renderer interface {
    Format() Format
    Render(io.Writer, View) error
}

Use the factory rather than constructing a concrete renderer directly:

outputRenderer, err := renderer.New(format)
if err != nil {
    return err
}

return outputRenderer.Render(os.Stdout, view)

io.Writer keeps rendering independent from stdout. It also allows callers to render into a buffer, file, pipe, or test fixture.

Views

View is a sealed interface. The package defines the supported view types in model.go:

  • ListView
  • ShowView
  • ShowCollectionView
  • SearchView
  • ContextView
  • LinksView
  • BacklinksView
  • GraphView
  • AnalyticsView
  • CheckView

The command layer constructs these views from knowledge-layer objects. Views should contain presentation-ready values such as titles, descriptions, IDs, links, actions, scores, and content.

Renderer files

File Responsibility
renderer.go Format parsing, factory, interface, shared helpers
model.go Typed presentation views
compact.go Minimal line-oriented and table-like output
fancy.go Rich terminal output and navigation actions
json.go JSON serialization
markdown.go Markdown serialization
agent.go Compact agent catalog serialization

Each renderer uses a type switch over the supported View types. The agent renderer intentionally supports ListView only; unsupported view types return an error instead of producing partial output.

Current output rules

Compact

Compact output is the default terminal format.

Recursive lists use an aligned table:

ID                                      TITLE
/general/comments                       Comments
/go/modern-features                     Modern Features

Details: manly show <ID>

Non-recursive lists label their mixed directory/concept columns accurately:

PATH                                    TITLE / CONCEPTS
/general/                               12 concepts
/general/comments                       Comments

Details: manly show <ID>

Search, links, and backlinks use aligned tables with explicit headers:

SCORE  ID                              TITLE
12.00  /engineering-preferences/react  React Preferences
LABEL          TARGET
related topic  /engineering/preferences
SOURCE                       LABEL
authoring/preferences         related topic

Other compact outputs remain line-oriented, such as graph and check.

Fancy

Fancy output keeps headings, descriptions, numbered links, backlinks, and navigation actions such as:

Open: manly show /concept
Context: manly context /concept
JSON

JSON output is the machine-readable contract. Changes to JSON field names or nesting should be treated as compatibility changes.

Agent

Agent output is a purpose-built machine-readable list contract. It is supported by list and emits only:

{
  "path": "/engineering-preferences/react",
  "recursive": true,
  "directories": [],
  "concepts": [
    {
      "id": "/engineering-preferences/react/hooks-as-controllers",
      "type": "React Guideline",
      "title": "Hooks as Controllers",
      "description": "Components should be thin UI...",
      "tags": ["react", "hooks"]
    }
  ],
  "truncated": false
}

It omits filesystem roots, duplicate file paths, concept bodies, relationships, actions, and usage hints. Other commands intentionally do not receive invented agent representations.

Markdown

Markdown output is intended for embedding in documents. It should remain valid, readable Markdown rather than terminal-oriented output.

How to adjust existing output

  1. Find the format file for the behavior:
    • compact: compact.go
    • fancy: fancy.go
    • JSON: json.go
    • Markdown: markdown.go
    • Agent catalog: agent.go
  2. Update only that renderer unless the output contract intentionally changes across formats.
  3. Preserve the view model as the source of data. Do not load bundles or query knowledge from a renderer.
  4. Keep output directed to the supplied io.Writer.
  5. Preserve existing machine-readable JSON fields unless the change is deliberate and documented.
  6. Update the relevant CLI or process-level tests when output behavior changes.

For example, changing compact list spacing belongs in compact.go, not in cmd/manly/command_list_show.go.

How to add a new command output

Suppose a new command needs a StatsView.

1. Add a typed view

Add the presentation model to model.go:

type StatsView struct {
    Concepts int
    Links    int
}

func (StatsView) view() {}

The unexported view method keeps the View interface sealed to this package.

2. Add support to every renderer

Add a StatsView case to Render in:

  • compact.go
  • fancy.go
  • json.go
  • markdown.go

Each case should call a focused format-specific function:

case StatsView:
    return renderCompactStats(w, value)

Do not silently fall back to another format. Every supported command view should have an intentional representation in every format.

3. Build the view in cmd/manly

The command layer converts knowledge data into renderer.StatsView and invokes the existing factory flow:

view := renderer.StatsView{
    Concepts: len(bundle.Concepts),
    Links:    linkCount,
}

outputRenderer, err := renderer.New(format)
if err != nil {
    return err
}
return outputRenderer.Render(os.Stdout, view)
4. Update documentation and tests

Update:

  • CLI usage or README examples when the user-facing contract changes.
  • Renderer or CLI tests for each format.
  • docs/v1.1.0-technical-review.md when the architecture or output contract changes.

Design rules

  • Keep domain logic in internal/knowledge.
  • Keep presentation logic in this package.
  • Do not import internal/knowledge here.
  • Do not parse flags here.
  • Do not write directly to os.Stdout; use the supplied writer.
  • Do not use a generic untyped payload instead of a typed view.
  • Keep JSON output stable.
  • Add compile-time interface assertions for renderer implementations:
var _ Renderer = compactRenderer{}
  • When adding a view, update all four format renderers and their tests.

Verification

From the repository root:

make test
go build ./...

The current repository has CLI and process-level coverage for the major commands and formats. Dedicated unit test files inside internal/renderer should be added when renderer behavior requires exact output or writer-error coverage.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action struct {
	Name    string `json:"name"`
	Command string `json:"command"`
}

Action contains a navigational CLI action.

type AnalyticsBatch added in v1.4.0

type AnalyticsBatch struct {
	BatchID      string    `json:"batch_id"`
	OccurredAt   time.Time `json:"occurred_at"`
	EntryPoint   string    `json:"entry_point"`
	ConceptCount int       `json:"concept_count"`
	ConceptIDs   []string  `json:"concept_ids"`
}

AnalyticsBatch contains one recent retrieval group.

type AnalyticsConcept added in v1.4.0

type AnalyticsConcept struct {
	ConceptID string `json:"concept_id"`
	LoadCount int    `json:"load_count"`
}

AnalyticsConcept contains one concept's usage count.

type AnalyticsPeriod added in v1.4.0

type AnalyticsPeriod struct {
	Since *time.Time `json:"since"`
}

AnalyticsPeriod identifies the lower bound used for a report.

type AnalyticsView added in v1.4.0

type AnalyticsView struct {
	Enabled                 bool               `json:"enabled"`
	Provider                string             `json:"provider,omitempty"`
	Period                  AnalyticsPeriod    `json:"period"`
	ConceptLoads            int                `json:"concept_loads"`
	RetrievalBatches        int                `json:"retrieval_batches"`
	AverageConceptsPerBatch float64            `json:"average_concepts_per_batch"`
	EntryPoints             map[string]int     `json:"entry_points"`
	TopConcepts             []AnalyticsConcept `json:"top_concepts"`
	RecentBatches           []AnalyticsBatch   `json:"recent_batches"`
}

AnalyticsView contains local concept-usage analytics.

type BacklinksView

type BacklinksView struct {
	Target    string `json:"target"`
	Backlinks []Link `json:"backlinks"`
}

BacklinksView contains incoming links for one concept.

type CheckBundle

type CheckBundle struct {
	Name                  string `json:"name"`
	Root                  string `json:"root"`
	MarkdownFiles         int    `json:"markdown_files"`
	ReservedFiles         int    `json:"reserved_files"`
	ConceptFiles          int    `json:"concept_files"`
	LoadedConcepts        int    `json:"loaded_concepts"`
	InvalidConceptFiles   int    `json:"invalid_concept_files"`
	LinksChecked          int    `json:"links_checked"`
	BrokenLinks           int    `json:"broken_links"`
	StaleGeneratedIndexes int    `json:"stale_generated_indexes"`
}

CheckBundle contains per-bundle validation statistics.

type CheckStats

type CheckStats struct {
	Bundles               int `json:"bundles"`
	MarkdownFiles         int `json:"markdown_files"`
	ReservedFiles         int `json:"reserved_files"`
	ConceptFiles          int `json:"concept_files"`
	LoadedConcepts        int `json:"loaded_concepts"`
	InvalidConceptFiles   int `json:"invalid_concept_files"`
	LinksChecked          int `json:"links_checked"`
	BrokenLinks           int `json:"broken_links"`
	StaleGeneratedIndexes int `json:"stale_generated_indexes"`
	Errors                int `json:"errors"`
	Warnings              int `json:"warnings"`
}

CheckStats contains aggregate validation and scan statistics.

type CheckView

type CheckView struct {
	Root     string        `json:"root"`
	Mode     string        `json:"mode"`
	Stats    CheckStats    `json:"stats"`
	Bundles  []CheckBundle `json:"bundles,omitempty"`
	Errors   []Issue       `json:"Errors"`
	Warnings []Issue       `json:"Warnings"`
	Valid    bool          `json:"valid"`
}

CheckView contains bundle validation results.

type Concept

type Concept struct {
	ID          string   `json:"id"`
	Path        string   `json:"path"`
	Type        string   `json:"type,omitempty"`
	Title       string   `json:"title"`
	Description string   `json:"description,omitempty"`
	Tags        []string `json:"tags,omitempty"`
	Content     string   `json:"content,omitempty"`
}

Concept contains presentation-ready concept metadata and content.

type ContextResult

type ContextResult struct {
	Concept       Concept  `json:"concept"`
	Score         float64  `json:"score"`
	MatchedFields []string `json:"matched_fields,omitempty"`
	MatchedTerms  []string `json:"matched_terms,omitempty"`
	MatchedRank   string   `json:"matched_rank,omitempty"`
	Confidence    string   `json:"confidence,omitempty"`
	Bundle        string   `json:"bundle,omitempty"`
	Links         []Link   `json:"links"`
	Actions       []Action `json:"actions"`
}

ContextResult contains one context concept and its links.

type ContextView

type ContextView struct {
	Query     string          `json:"query"`
	Source    SourceInfo      `json:"source"`
	Confident bool            `json:"confident"`
	Results   []ContextResult `json:"results"`
}

ContextView contains bounded context results for a query.

type Directory

type Directory struct {
	Path  string `json:"path"`
	Count int    `json:"count"`
}

Directory contains a directory path and its concept count.

type Format

type Format string

Format identifies an output representation.

const (
	FormatCompact  Format = "compact"
	FormatFancy    Format = "fancy"
	FormatJSON     Format = "json"
	FormatMarkdown Format = "markdown"
	FormatAgent    Format = "agent"
)

func ParseFormat

func ParseFormat(value string) (Format, error)

ParseFormat validates a user-provided output format.

type GraphNode

type GraphNode struct {
	ID    string `json:"id"`
	Title string `json:"title"`
	Depth int    `json:"depth"`
}

GraphNode contains one concept and its traversal depth.

type GraphView

type GraphView struct {
	Nodes []GraphNode `json:"nodes"`
}

GraphView contains graph traversal nodes.

type Issue

type Issue struct {
	Path    string `json:"path"`
	Message string `json:"message"`
}

Issue contains one validation issue.

type Link struct {
	Label      string `json:"label"`
	Title      string `json:"-"`
	Target     string `json:"target,omitempty"`
	TargetPath string `json:"target_path,omitempty"`
	URL        string `json:"url,omitempty"`
	Broken     bool   `json:"broken,omitempty"`
	External   bool   `json:"external,omitempty"`
}

Link contains presentation-ready link information.

type LinksView

type LinksView struct {
	Source string `json:"source"`
	Links  []Link `json:"links"`
}

LinksView contains outgoing links for one concept.

type ListEntry

type ListEntry struct {
	Concept Concept  `json:"concept"`
	Actions []Action `json:"actions,omitempty"`
}

ListEntry contains one concept and its available actions.

type ListView

type ListView struct {
	Root        string      `json:"root"`
	Path        string      `json:"path"`
	Heading     string      `json:"heading,omitempty"`
	Recursive   bool        `json:"recursive"`
	Directories []Directory `json:"directories"`
	Entries     []ListEntry `json:"entries"`
	Count       int         `json:"count,omitempty"`
	HideActions bool        `json:"-"`
	HideUsage   bool        `json:"-"`
}

ListView contains directory listing data.

type Renderer

type Renderer interface {
	Format() Format
	Render(io.Writer, View) error
}

Renderer writes one output format for a typed view.

func New

func New(format Format) (Renderer, error)

New creates the renderer for format.

type SearchResult

type SearchResult struct {
	Concept       Concept  `json:"concept"`
	Score         float64  `json:"score"`
	MatchedFields []string `json:"matched_fields,omitempty"`
	MatchedTerms  []string `json:"matched_terms,omitempty"`
	MatchedRank   string   `json:"matched_rank,omitempty"`
	Confidence    string   `json:"confidence,omitempty"`
	Bundle        string   `json:"bundle,omitempty"`
	Actions       []Action `json:"actions"`
}

SearchResult contains one scored search result.

type SearchView

type SearchView struct {
	Query     string         `json:"query"`
	Source    SourceInfo     `json:"source"`
	Confident bool           `json:"confident"`
	Results   []SearchResult `json:"results"`
}

SearchView contains search results for a query.

type ShowCollectionView

type ShowCollectionView struct {
	Results []ShowResult `json:"results"`
}

ShowCollectionView contains multiple complete concepts and their relationships.

type ShowResult

type ShowResult struct {
	Concept   Concept  `json:"concept"`
	Links     []Link   `json:"links"`
	Backlinks []Link   `json:"backlinks"`
	Actions   []Action `json:"actions,omitempty"`
	HideUsage bool     `json:"-"`
}

ShowResult contains one concept and its relationships in a collection.

type ShowView

type ShowView struct {
	Concept     Concept  `json:"concept"`
	Links       []Link   `json:"links"`
	Backlinks   []Link   `json:"backlinks"`
	Actions     []Action `json:"actions,omitempty"`
	HideActions bool     `json:"-"`
	HideUsage   bool     `json:"-"`
}

ShowView contains one complete concept and its relationships.

type SourceInfo added in v1.3.0

type SourceInfo struct {
	Root string `json:"root"`
	Mode string `json:"mode"`
}

SourceInfo identifies where results came from.

type View

type View interface {
	// contains filtered or unexported methods
}

View is a typed presentation model accepted by a Renderer.

Jump to

Keyboard shortcuts

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