trellis

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jan 10, 2026 License: AGPL-3.0 Imports: 10 Imported by: 2

README

Trellis

Go Report Card Go Doc License Release

"Faça uma coisa e faça bem feita. Trabalhe com fluxos de texto." - Filosofia Unix

Trellis é um Motor de Máquina de Estados Determinístico (Deterministic State Machine Engine) para a construção de CLIs, fluxos de automação e Guardrails para Agentes de IA.

Ele atua como a espinha dorsal lógica do seu sistema: enquanto sua interface (ou LLM) gerencia a conversa, o Trellis impõe estritamente as regras de negócio e as transições permitidas.

Principais Features

  • Strict Typing & Serialization: Garante que seus fluxos sejam robustos e livres de erros de digitação.
  • Namespaces (Sub-Grafos): Organize fluxos complexos em pastas e módulos (jump_to), escalando sua arquitetura.
  • MCP Server: Integração nativa com Model Context Protocol para conectar Agentes de IA (Claude, etc.).
  • Hexagonal Architecture: Core agnóstico (Go Library) desacoplado de FileSystem (Loam Adapter).
  • Hot Reload: Desenvolva com feedback instantâneo.

Quick Start

Instalação
git clone https://github.com/aretw0/trellis
cd trellis
go mod tidy
Rodando o Golden Path (Demo)
# Execução do Engine (Demo)
go run ./cmd/trellis ./examples/tour

Usage

Running a Flow
# Interactive mode
trellis run ./my-flow

# HTTP Server Mode (Stateless)
trellis serve --dir ./my-flow --port 8080
# Swagger UI available at: http://localhost:8080/swagger

# MCP Server Mode (Claude Desktop / Agent)
trellis mcp --dir ./my-flow
# Or with SSE:
trellis mcp --dir ./my-flow --transport sse --port 8080
Introspection

Visualize your flow as a Mermaid graph:

trellis graph ./my-flow
# Outputs: graph TD ...
Development Mode

Using Makefile (Recommended):

make gen    # Generate Go code from OpenAPI spec
make serve  # Run server with 'tour' example
make test   # Run tests

Manual Hot Reload: Iterate faster on your flows by watching for file changes:

trellis run --watch --dir ./my-flow

The engine will monitor your .md, .json, .yaml, and .yml files. When you save a change, the session will automatically reload (preserving the workflow loop).

Documentação

Estrutura

trellis/
├── cmd/           # Entrypoints (trellis CLI)
├── docs/          # Documentação do Projeto
├── examples/      # Demos e Receitas (Tours, Patterns)
├── internal/      # Implementação Privada (Loam Adapter, Runtime)
├── pkg/           # Contratos Públicos (Facade, Domain, Ports)
└── tests/         # Testes de Integração (Certification Suite)

Library vs Framework

O Trellis foi desenhado para ser usado de duas formas:

  1. Como Framework (CLI): Use o executável trellis para rodar pastas de Markdown (loam). Ótimo para scripts rapidos e prototipagem.
  2. Como Biblioteca (Go): Importe github.com/aretw0/trellis e instancie o Engine dentro do seu binário. Você pode injetar grafos em memória, usar banco de dados ou qualquer outra fonte, sem depender de arquivos ou do Loam.

"Opinionated by default (Loam), flexible under the hood (Memory/Custom)."

Documentation

Overview

Package trellis is a state machine engine designed for building text-based adventure games, interactive stories, and complex conversational flows.

It provides a flexible runtime that separates the narrative graph definition from the execution state, enabling rich, logic-driven navigation.

Key Features

  • Graph-based State Machine: Define complex flows with nodes and transitions.
  • Pluggable Loaders: Load graphs from the filesystem (Markdown/Frontmatter) or in-memory structures.
  • Conditional Logic: Dynamic transitions based on custom evaluators.
  • State Management: Serializable state for long-running sessions.

Basic Usage

Initialize the engine seamlessly using the filesystem loader (powered by Loam):

eng, err := trellis.New("./story-data")
if err != nil {
    log.Fatal(err)
}

state := eng.Start()

// Render the initial view
actions, _, err := eng.Render(context.Background(), state)

// Navigate based on input
nextState, err := eng.Navigate(context.Background(), state, "open door")

For advanced usage, including custom loaders or conditional logic, refer to the examples and sub-packages.

Index

Examples

Constants

This section is empty.

Variables

View Source
var Version string

Functions

This section is empty.

Types

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the high-level entry point for the Trellis library. It wraps the internal runtime and provides a simplified API for consumers.

func New

func New(repoPath string, opts ...Option) (*Engine, error)

New initializes a new Trellis Engine. By default, it uses a Loam repository at the given path. If WithLoader option is provided, repoPath can be empty and Loam is skipped.

Example (Memory)

ExampleNew_memory demonstrates how to use the Engine with an in-memory graph definition. This is useful for testing, embedded scenarios, or when you don't want to rely on the file system.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aretw0/trellis"
	"github.com/aretw0/trellis/pkg/adapters/memory"
	"github.com/aretw0/trellis/pkg/domain"
)

func main() {
	// 1. Define your graph using helper NewFromNodes for clean, type-safe construction.
	loader, err := memory.NewFromNodes(
		domain.Node{
			ID:      "start",
			Type:    "question",
			Content: []byte("Hello! Do you want to proceed? [yes] [no]"),
			Transitions: []domain.Transition{
				{ToNodeID: "yes", Condition: "input == 'yes'"},
				{ToNodeID: "no"},
			},
		},
		domain.Node{
			ID:      "yes",
			Type:    "text",
			Content: []byte("Great! You moved forward."),
		},
		domain.Node{
			ID:      "no",
			Type:    "text",
			Content: []byte("Okay, bye."),
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	// 2. Initialize Trellis with the custom loader
	// Note: We leave path empty ("") because we are providing a loader.
	engine, err := trellis.New("", trellis.WithLoader(loader))
	if err != nil {
		log.Fatal(err)
	}

	// 4. Start the flow
	state := engine.Start()
	ctx := context.Background()

	// 5. Navigate (Input: "yes")
	// "start" -> (input: yes) -> "yes"
	// Note: Example previously captured actions from Step.
	// Render to show we can get actions, then Navigate.
	actions, _, err := engine.Render(ctx, state)
	if err != nil {
		log.Fatal(err)
	}
	// Verify actions from start node (optional in example, but good for completeness)

	nextState, err := engine.Navigate(ctx, state, "yes")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Current Node: %s\n", nextState.CurrentNodeID)
	for _, action := range actions {
		fmt.Printf("Action: %s\n", action.Type)
	}
}
Output:
Current Node: yes
Action: RENDER_CONTENT

func (*Engine) Inspect

func (e *Engine) Inspect() ([]domain.Node, error)

Inspect returns the full graph definition for visualization or introspection tools.

func (*Engine) Loader added in v0.3.3

func (e *Engine) Loader() ports.GraphLoader

Loader returns the underlying GraphLoader used by the engine.

func (*Engine) Navigate added in v0.3.2

func (e *Engine) Navigate(ctx context.Context, state *domain.State, input any) (*domain.State, error)

Navigate determines the next state based on input.

func (*Engine) Render added in v0.3.2

func (e *Engine) Render(ctx context.Context, state *domain.State) ([]domain.ActionRequest, bool, error)

Render generates the actions (view) for the current state without transitioning. Returns actions, isTerminal (true if no transitions), and error.

func (*Engine) Start

func (e *Engine) Start() *domain.State

Start creates the initial state for the flow. It acts as a factory for the first generic State.

func (*Engine) Watch added in v0.3.2

func (e *Engine) Watch(ctx context.Context) (<-chan string, error)

Watch returns a channel that signals when the underlying graph changes. Returns error if the loader does not support watching.

type Option

type Option func(*Engine)

Option defines a functional option for configuring the Engine.

func WithConditionEvaluator

func WithConditionEvaluator(eval runtime.ConditionEvaluator) Option

WithConditionEvaluator sets a custom logic evaluator for the engine.

func WithLoader added in v0.3.1

func WithLoader(l ports.GraphLoader) Option

WithLoader injects a custom GraphLoader, bypassing the default Loam initialization.

Directories

Path Synopsis
cmd
trellis command
examples
hello-world command
low-level-api command
internal
adapters/http
Package http provides primitives to interact with the openapi HTTP API.
Package http provides primitives to interact with the openapi HTTP API.
cli
dto
pkg

Jump to

Keyboard shortcuts

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