enno

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: MIT Imports: 10 Imported by: 0

README

Enno

Enno is a lightweight Go agent framework that can be embedded as a package or installed as a CLI agent.

It provides a provider-agnostic Agent loop, a composable tool system, built-in OpenAI-compatible and Anthropic providers, and optional tools for a persistent task graph (task_create / task_update / task_list / task_get), filesystem access, shell execution, ripgrep-based content search (grep), and ripgrep-based file globbing (glob).

Repository: github.com/dean2021/enno

Features

  • Provider-neutral core package: Agent, Provider, Tool, Message, Request, and Response.
  • OpenAI-compatible provider via provider/openai.
  • Anthropic Messages API provider via provider/anthropic.
  • Optional built-in tools:
    • tools/taskgraph (DAG task tools; CLI stores under ~/.enno/tasks/<session_id>/, default on, disable with task_graph: false or --no-task-graph)
    • tools/filesystem
    • tools/shell
    • tools/grep (grep: regex search via system rg; CLI default on, disable with grep: false or --no-grep)
    • tools/glob (glob: file patterns via rg --files; CLI default on, disable with glob: false or --no-glob)
    • tools/subagent (subagent tool: isolated child agent; CLI enables via subagent: true in config)
    • tools/loadskill (load_skill + SKILL.md trees; CLI: skills_dir in config or --skills-dir)
    • tools/compact + Config.Compaction: optional context compression (micro tool-result trimming, auto summarization, manual compact); default off, configured via YAML or struct
  • Optional Agent events for observing model calls, tool calls, results, and token usage.
  • Installable CLI at cmd/enno.
  • Extensible tool and provider interfaces for custom integrations.

Installation

Install the CLI:

go install github.com/dean2021/enno/cmd/enno@latest

Use as a Go package:

go get github.com/dean2021/enno

Install a specific version:

go install github.com/dean2021/enno/cmd/enno@latest
go get github.com/dean2021/enno@latest

CLI Usage

Start the tview terminal UI interactive mode:

enno

Type a task and press Enter. Use Esc, Ctrl+C, q, or exit to leave the interactive UI. The main Enno window shows a single conversation stream: user prompts, model progress, tool calls, tool arguments, muted tool results, and final answers are appended in order so current activity stays visible. Scroll the transcript only with the mouse wheel while the pointer is over the main window (keyboard does not scroll it). Browse prior prompts with / in the input line. Text selection with mouse tracking enabled may require Shift+drag in some terminals. It does not display hidden model chain-of-thought.

Run a single prompt:

enno run "Analyze this repository"

Configure the CLI in ~/.enno/config.yaml. Skills load from ~/.enno/skills by default (merge with optional skills_extra_dirs and --skills-dir). Set subagent: true to register the subagent tool (isolated child agent per delegation). If the default config file does not exist, Enno creates a commented template on startup:

provider: openai
model: your-model
api_key: your-key
base_url: https://example.com/v1
max_tokens: 4096
shell: true
filesystem: true

Use a custom config file:

enno --config /path/to/config.yaml
enno run --config /path/to/config.yaml "Analyze this repository"

Common flags:

enno --workdir .
enno --no-shell
enno --no-filesystem

Provider, model, API key, base URL, and max token settings are read from config.yaml only. The CLI does not read ENNO_* environment variables for these values.

Package Usage

package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/dean2021/enno"
	openaiprovider "github.com/dean2021/enno/provider/openai"
	"github.com/dean2021/enno/tools/filesystem"
	"github.com/dean2021/enno/tools/taskgraph"
)

func main() {
	tools := taskgraph.New(taskgraph.Config{Root: ".", Timeout: 120 * time.Second})
	tools = append(tools, filesystem.New(filesystem.Config{Root: "."})...)

	agent, err := enno.NewAgent(enno.Config{
		Provider: openaiprovider.New(openaiprovider.Config{
			APIKey:  os.Getenv("ENNO_API_KEY"),
			BaseURL: os.Getenv("ENNO_BASE_URL"),
			Model:   os.Getenv("ENNO_MODEL"),
		}),
		SystemPrompt: "You are a helpful coding agent.",
		Tools:        tools,
	})
	if err != nil {
		panic(err)
	}

	answer, err := agent.Run(context.Background(), "List the files in this workspace.")
	if err != nil {
		panic(err)
	}
	fmt.Println(answer)
}

Anthropic Provider

agent, err := enno.NewAgent(enno.Config{
	Provider: anthropicprovider.New(anthropicprovider.Config{
		APIKey:    os.Getenv("ANTHROPIC_API_KEY"),
		Model:     "claude-sonnet-4-5-20250929",
		MaxTokens: 4096,
	}),
	SystemPrompt: "You are a helpful agent.",
	Tools:        taskgraph.New(taskgraph.Config{Root: ".", Timeout: 120 * time.Second}),
})

Custom Tools

type GreetArgs struct {
	Name string `json:"name"`
}

greet := enno.NewTypedTool("greet", "Greet a person by name.", map[string]any{
	"name": map[string]any{"type": "string"},
}, []string{"name"}, func(ctx context.Context, args GreetArgs) (string, error) {
	return "Hello, " + args.Name + "!", nil
})

Then pass the tool to an agent:

agent, err := enno.NewAgent(enno.Config{
	Provider: provider,
	Tools:    []enno.Tool{greet},
})

Observability

Attach an optional event handler to observe model calls, tool calls, tool results, and token usage:

agent, err := enno.NewAgent(enno.Config{
	Provider: provider,
	Tools:    tools,
	EventHandler: func(ctx context.Context, event enno.Event) {
		fmt.Printf("%s round=%d usage=%+v\n", event.Type, event.Round, event.Usage)
	},
})

Events expose observable execution details and model-visible content only. They do not expose hidden model chain-of-thought.

The CLI renders these events directly in the main Enno conversation stream, similar to a coding-agent transcript: model progress, tool calls, parameters, and muted results appear inline as they happen.

Architecture

enno/
  agent.go              core Agent loop
  config.go             Agent configuration
  message.go            provider-neutral messages
  tool.go               tool declaration and execution API
  provider_iface.go     provider interface

  provider/openai       OpenAI-compatible provider
  provider/anthropic    Anthropic provider
  tools/taskgraph       persistent DAG task tools (task_*)
  tools/filesystem      file read/write/edit tools
  tools/shell           shell tool
  internal/cliui        CLI-only terminal UI
  internal/cliconfig    CLI-only configuration parsing
  cmd/enno              installable CLI
  examples              usage examples
  docs                  design and usage documentation

See:

Versioning

Enno follows Semantic Versioning. The current initial release line is v0.x.y while the public API is still evolving.

Useful release commands:

make help
make version
make release-check
make tag

make tag creates a Git tag for the version in VERSION, such as v0.4.0. Pushing the tag triggers the release workflow.

Safety Notes

  • Avoid enabling tools/shell in production without sandboxing.
  • Always configure tools/filesystem with a restricted root directory.
  • Do not hard-code API keys in source code.
  • Use separate Agent instances for independent user sessions.

License

Enno is released under the MIT License.

Documentation

Index

Constants

View Source
const CompactionToolName = "compact"

CompactionToolName is the registered tool name for manual compaction (same string used by tools/compact).

View Source
const DefaultMaxToolRounds = 50

Variables

View Source
var ErrMissingProvider = errors.New("enno: missing provider")

Functions

func FormatCompactSummary added in v0.5.0

func FormatCompactSummary(raw string) string

FormatCompactSummary strips <analysis>, extracts <summary> body when present, and normalizes whitespace. If no <summary> tag is found, returns the whole string trimmed (backward compatible).

func ToolMap

func ToolMap(tools []Tool) map[string]Tool

Types

type Agent

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

func NewAgent

func NewAgent(config Config) (*Agent, error)

func (*Agent) Messages

func (a *Agent) Messages() []Message

func (*Agent) Reset

func (a *Agent) Reset()

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, input string) (string, error)

type CompactionConfig added in v0.5.0

type CompactionConfig struct {
	Enabled bool

	// TranscriptDir stores JSONL transcripts before summarization. Empty uses
	// ~/.enno/transcripts when Enabled (after withDefaults).
	TranscriptDir string

	// ModelContextTokens, when positive, sets auto-compact threshold to
	// ModelContextTokens - AutoCompactBufferTokens (buffer defaults to 13000 in withDefaults).
	// Takes precedence over AutoCompactInputTokens when the difference is positive.
	ModelContextTokens int64

	// AutoCompactBufferTokens is subtracted from ModelContextTokens for the effective threshold.
	// Ignored when ModelContextTokens is zero. Zero defaults to 13000 when ModelContextTokens > 0.
	AutoCompactBufferTokens int64

	// AutoCompactInputTokens triggers summarization when estimated/conservative input tokens
	// meet or exceed this value. Zero defaults to 50000 when ModelContextTokens is zero.
	AutoCompactInputTokens int64

	// KeepRecentToolResults is how many latest eligible RoleTool messages keep full content in Micro.
	// Zero defaults to 3.
	KeepRecentToolResults int

	// MicroCompactMinChars replaces longer RoleTool contents with a placeholder. Zero defaults to 100.
	MicroCompactMinChars int

	// MicroCompactToolNames, when non-empty, limits Micro compaction to tool results whose tool name
	// is in this list. Empty means all RoleTool messages participate (legacy behavior).
	MicroCompactToolNames []string

	// SkipOnSummarizeError, when true, causes automatic compaction to log an error event and continue
	// without replacing history if summarization fails. Manual compact via the compact tool remains strict.
	SkipOnSummarizeError bool
}

CompactionConfig enables optional context compaction (micro-trimming of old tool results, automatic summarization when estimated input size exceeds a threshold, and the manual compact tool). Nil in Config means compaction is disabled.

type Config

type Config struct {
	Provider      Provider
	SystemPrompt  string
	Tools         []Tool
	MaxToolRounds int
	EventHandler  EventHandler
	Compaction    *CompactionConfig
}

type Event added in v0.3.0

type Event struct {
	Type         EventType
	Round        int
	MessageCount int
	ToolCount    int
	Content      string
	Thinking     string
	ToolCall     ToolCall
	ToolResult   string
	Usage        Usage
	Duration     time.Duration
	Err          error
}

type EventHandler added in v0.3.0

type EventHandler func(context.Context, Event)

type EventType added in v0.3.0

type EventType string
const (
	EventModelStart    EventType = "model_start"
	EventModelResponse EventType = "model_response"
	EventToolStart     EventType = "tool_start"
	EventToolResult    EventType = "tool_result"
	EventRoundComplete EventType = "round_complete"
	EventError         EventType = "error"
)

type Message

type Message struct {
	Role       Role
	Content    string
	ToolCallID string
	ToolCalls  []ToolCall
}

func AssistantMessage

func AssistantMessage(content string, toolCalls []ToolCall) Message

func ToolMessage

func ToolMessage(toolCallID, content string) Message

func UserMessage

func UserMessage(content string) Message

type Provider

type Provider interface {
	Complete(ctx context.Context, req Request) (Response, error)
}

type Request

type Request struct {
	SystemPrompt string
	Messages     []Message
	Tools        []Tool
}

type Response

type Response struct {
	Content   string
	Thinking  string
	ToolCalls []ToolCall
	Usage     Usage
}

type Role

type Role string
const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
)

type Tool

type Tool struct {
	Name        string
	Description string
	Properties  map[string]any
	Required    []string
	Handler     ToolHandler
}

func NewTool

func NewTool(name, description string, properties map[string]any, required []string, handler ToolHandler) Tool

func NewTypedTool

func NewTypedTool[T any](name, description string, properties map[string]any, required []string, handler func(context.Context, T) (string, error)) Tool

type ToolCall

type ToolCall struct {
	ID        string
	Name      string
	Arguments json.RawMessage
}

type ToolHandler

type ToolHandler func(context.Context, json.RawMessage) (string, error)

type Usage added in v0.3.0

type Usage struct {
	InputTokens  int64
	OutputTokens int64
	TotalTokens  int64
	Estimated    bool
}

func EstimateUsage added in v0.3.0

func EstimateUsage(req Request) Usage

Directories

Path Synopsis
cmd
enno command
examples
anthropic command
custom_tool command
loadskill command
simple_agent command
subagent command
internal
tools
glob
Package glob provides a read-only file listing tool backed by ripgrep (rg --files).
Package glob provides a read-only file listing tool backed by ripgrep (rg --files).
grep
Package grep provides a read-only content search tool backed by ripgrep (rg).
Package grep provides a read-only content search tool backed by ripgrep (rg).
taskgraph
Package taskgraph provides persistent DAG task tools (task_create / task_update / task_list / task_get) backed by JSON files under a configurable directory, aligned with s07-style task systems.
Package taskgraph provides persistent DAG task tools (task_create / task_update / task_list / task_get) backed by JSON files under a configurable directory, aligned with s07-style task systems.

Jump to

Keyboard shortcuts

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