Build a Custom Adapter

Use cli.BuildRuntime when your program provides the interface but should still use caretz’s CLI flags and configuration. This complete example implements a small line-oriented stdin adapter. It stores its session under the stdin adapter namespace and gives it a dedicated workspace.

Create a project as in Extend the standard CLI , then save this as main.go:

package main

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/makkalot/caretz/cli"
	"github.com/makkalot/caretz/lib/eventing"
)

func main() {
	os.Exit(run())
}

func run() int {
	ctx := context.Background()
	rt, code := cli.BuildRuntime(ctx, os.Args[1:], os.Stderr, cli.Options{})
	if code != 0 {
		return code
	}

	workspace := filepath.Join(rt.CWD(), "workspaces", "stdin", "local")
	if err := os.MkdirAll(workspace, 0o755); err != nil {
		fmt.Fprintf(os.Stderr, "create workspace: %v\n", err)
		return 1
	}

	session, err := rt.NewAdapterSession("stdin", "local", cli.AdapterSessionOptions{
		WorkspaceDir: workspace,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "open session: %v\n", err)
		return 1
	}

	fmt.Println("Enter a prompt, or press Ctrl+D to exit.")
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		prompt := strings.TrimSpace(scanner.Text())
		if prompt == "" {
			continue
		}

		event := eventing.NewLogMessage("local", prompt)
		if err := session.AppendAuditEvent(event); err != nil {
			fmt.Fprintf(os.Stderr, "save prompt: %v\n", err)
			return 1
		}
		response, err := session.ProcessUserEvent(ctx, event)
		if err != nil {
			fmt.Fprintf(os.Stderr, "run agent: %v\n", err)
			return 1
		}
		if response != nil {
			fmt.Println(response.Content)
		}
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintf(os.Stderr, "read stdin: %v\n", err)
		return 1
	}
	return 0
}

Build and run it:

go build -o caretz-stdin .
./caretz-stdin

BuildRuntime accepts caretz’s runtime flags, such as --workspace, --model, --config-root, and verbosity flags. Unlike cli.Run, it does not accept prompt-mode or session-maintenance commands because your adapter owns input and output.

The returned runtime exposes Context, CWD, SessionsDir, Logger, and NewAdapterSession. A production adapter is responsible for receiving input, choosing stable channel IDs, creating per-channel workspaces, durably appending incoming messages before processing them, delivering responses, retrying where appropriate, and handling shutdown. lib/conversation.Runtime provides worker and retry coordination for event-driven transports.

For a fuller adapter, see vault . It is an assistant for a folder of Markdown notes, with capture, ask, and serve commands. Adapter sessions come with caretz’s scheduler tools, so serve gets its daily plan and weekly review just by asking the agent to schedule them.