carlos

module
v0.8.25 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: GPL-3.0

README

carlos

carlos

coverage

A pure-Go TUI agent. Single binary around 25 MB. No CGO. Cross-compiled for darwin + linux × amd64 + arm64.

Marketing copy and feature tour live at georgebuilds.github.io/carlos. This README is for getting carlos running and for Go developers who want to contribute.


Quick start

Install
brew install georgebuilds/tap/carlos
carlos

Or grab a tarball from Releases and drop carlos into your $PATH.

First run

Onboarding takes ~30 seconds:

  1. Your name
  2. A provider (Anthropic, OpenAI, OpenRouter, Ollama, or Gemini) and an API key
  3. A model from a curated dropdown (OpenRouter shows live pricing)
  4. Whether to enable the background daemon (scheduled runs, gateway delivery)
  5. Optional Obsidian vault path

Everything lands in ~/.carlos/config.yaml (mode 0600). Re-enter any single screen later with carlos onboard --only providers (or models, daemon, gateway, etc.).

After onboarding you're in the chat TUI. Type a question. carlos answers, calls tools when needed (prompting for the risky ones), and keeps the transcript in a SQLite event log at ~/.carlos/state.db.

Keys worth knowing
Key What
Ctrl+F open the frame switcher
/help full slash-command list
!<cmd> run a shell command in your context
/agents open the sub-agent manage view
/whoami current frame, mode, provider, model
/permissions layered approval state + audit log
/mcp list configured MCP servers and their tools
Demo: the user-shell

A !-prefixed line runs in your context without leaving the chat. Enter runs it in the foreground; Ctrl+Z pushes a running job to the background; Ctrl+J opens the jobs overlay (j/k to navigate, d to cancel, Esc to close). The slash variants are /shell <cmd>, /bg, and /jobs. A background job can also outlive the session entirely: carlos run "<cmd>" hands it to the daemon, and carlos jobs / carlos attach <id> / carlos logs <id> / carlos stop <id> drive it from anywhere.

The recording is scripted in demos/usershell-jobs.tape; regenerate it with vhs demos/usershell-jobs.tape (requires charmbracelet/vhs).

CLI verbs adjacent to the chat
carlos please "<prompt>"         # one-shot, no TUI
carlos research "<question>"     # multi-phase deep research
carlos memory search <query>     # FTS5 over conversation summaries
carlos schedule list|add|rm      # cron + natural language
carlos gateway add               # wizard to configure ntfy / Telegram / Signal
carlos gateway test <channel>    # verify ntfy / Telegram wiring
carlos daemon enable|disable     # background service

All accept -f <frame> (or --frame) to scope to a specific frame. carlos please takes a single positional prompt; multi-word prompts must be quoted (a single hyphenated token like say-hello works without quotes).

carlos please shows a live 3-row bordered status panel while it runs (current tool, streaming-text preview, tool counter + provider/model); the assistant's final reply prints below the panel on completion.

carlos research shows a live phase tracker (decompose, search, fetch, read, synthesize, verify) and writes a cited report under ~/.carlos/frames/<frame>/research/:


Contributing

Prerequisites
  • Go toolchain version pinned by go.mod (currently 1.26.3).
  • git on $PATH (the sub-agent sandbox uses git worktree).
  • No CGO, no system libraries.
Build and test
git clone https://github.com/georgebuilds/carlos
cd carlos
go test ./...
go build ./cmd/carlos
./carlos

Cross-compile checks are cheap; run them when touching anything that imports OS-specific paths:

GOOS=linux  GOARCH=arm64  go build ./cmd/carlos
GOOS=darwin GOARCH=amd64  go build ./cmd/carlos
Test discipline
  • go test ./... is the floor. Current count is ~5300 tests across 46 packages.
  • go vet ./... must be clean.
  • New code aims for 80%+ coverage on touched packages.
  • The sub-agent + daemon + event log paths have integration tests; if you touch any of them, run go test -race ./internal/agent/... ./internal/daemon/... at least once before pushing.
Repository layout
cmd/carlos/         main TUI binary + daemon entry points
internal/
  agent/            tool-use loop, event log, supervision, layered approval
  config/           ~/.carlos/config.yaml schema + onboarding state
  daemon/           background scheduler (UDS + launchd / systemd)
  farewell/         bordered exit-panel + brew-update probe
  frame/            per-session frames (personal + N user-defined)
  gateway/          chat-surface adapters (ntfy, Telegram, Signal stub)
  mcp/              Model Context Protocol client + tool adapter
  memory/           SQLite FTS5 + summarizer
  miniyaml/         hand-rolled YAML for frontmatter
  notes/            Obsidian vault index + cache (Goldmark + miniyaml)
  projectctx/       per-project context loader (walks AGENTS.md / CLAUDE.md)
  providers/        anthropic, openai, openrouter, ollama, gemini
  research/         decompose → search → fetch → read → synthesize → verify
  sandbox/          local + git-worktree execution
  schedule/         cron + natural-language grammar
  skills/           skill format, loader, inducer, judge, replay-eval
  theme/            light / dark / NO_COLOR / configurable accent
  tools/            every registered tool (notes_*, web_*, code_search, etc.)
  tui/              bubbletea chat / manage / onboarding / slash registry
  usershell/        `!` prefix shell driver, jobs overlay, history
  workspace/        trusted-workspaces store + read-only bash classifier
skills/             bundled starter skills (calendar/, ...)
docs/               GitHub Pages site + llms.txt
Where to look when adding a feature
You want to Look here
Add a tool internal/tools/, register in tools.go
Add a slash command internal/tui/slash/slash.go Builtins + handler in internal/tui/chat/
Wire an MCP server mcp_servers: block in ~/.carlos/config.yaml; client + adapter live in internal/mcp/
Add a provider internal/providers/<name>/, satisfy the Provider interface
Add a frame field internal/frame/frame.go, then sysprompt + render helpers
Change permission rules internal/agent/policy.go (LayeredApprover)
Add an event type internal/agent/eventlog_sqlite.go + state machine
Add an onboarding screen internal/tui/onboarding/screen_*.go + flow wiring
House conventions
  • Single immutable event log is the source of truth. The projection replays from it.
  • Atomic writes for any file containing user state: temp + fsync + rename. See internal/config/config.go:Save for the canonical recipe.
  • File modes: 0700 directories, 0600 secret-bearing files (config.yaml, state.db, trusted-workspaces.json, artifact blobs), 0644 elsewhere.
  • Prefer editing existing files to creating new ones.
  • Default to no comments; add one only when the WHY is non-obvious.
Release flow

A v* tag push fires .github/workflows/release.yml which runs goreleaser to build the four-arch tarballs, drafts a GitHub release, and bumps georgebuilds/homebrew-tap/Formula/carlos.rb. Publish the draft when ready (gh release edit v0.X.Y --draft=false).


License

GPL-3.0-or-later. See LICENSE.

Directories

Path Synopsis
cmd
carlos command
boottrace.go - slice 9f boot-performance instrumentation.
boottrace.go - slice 9f boot-performance instrumentation.
internal
agent
Phase 7 slice 7e - apply handler.
Phase 7 slice 7e - apply handler.
clipboard
Package clipboard is carlos's thin seam over the system clipboard, scoped to the one operation the chat composer needs: "is there an image on the clipboard right now, and if so give me its bytes".
Package clipboard is carlos's thin seam over the system clipboard, scoped to the one operation the chat composer needs: "is there an image on the clipboard right now, and if so give me its bytes".
config
Package config owns the on-disk schema for ~/.carlos/config.yaml.
Package config owns the on-disk schema for ~/.carlos/config.yaml.
daemon
Daemon-side wiring for the messaging gateway.
Daemon-side wiring for the messaging gateway.
farewell
Package farewell renders carlos's post-exit panel.
Package farewell renders carlos's post-exit panel.
frame
Package frame owns the "personal" + N user-defined frames model.
Package frame owns the "personal" + N user-defined frames model.
fuzzy
Package fuzzy is a small, dependency-free fuzzy matcher shared by the Ctrl+P command palette (slash-command names + descriptions) and @file mention autocomplete (file paths).
Package fuzzy is a small, dependency-free fuzzy matcher shared by the Ctrl+P command palette (slash-command names + descriptions) and @file mention autocomplete (file paths).
gateway
Package gateway provides the messaging-gateway broker and adapter contract that lets carlos talk to ntfy, Telegram, Signal, and a future custom HITL side-app over the same canonical envelope.
Package gateway provides the messaging-gateway broker and adapter contract that lets carlos talk to ntfy, Telegram, Signal, and a future custom HITL side-app over the same canonical envelope.
gateway/approvals
Package approvals bridges the agent.ApprovalQueue to the gateway Broker.
Package approvals bridges the agent.ApprovalQueue to the gateway Broker.
gateway/fake
Package fake provides a deterministic in-memory gateway.Adapter used by broker tests and by downstream packages (approvals router, daemon-side integration) to exercise the gateway loop end-to-end without a real platform behind it.
Package fake provides a deterministic in-memory gateway.Adapter used by broker tests and by downstream packages (approvals router, daemon-side integration) to exercise the gateway loop end-to-end without a real platform behind it.
gateway/ntfy
Package ntfy implements the gateway.Adapter for ntfy.sh.
Package ntfy implements the gateway.Adapter for ntfy.sh.
gateway/signal
Package signal is the Signal-channel gateway.Adapter.
Package signal is the Signal-channel gateway.Adapter.
gateway/telegram
Package telegram implements the gateway.Adapter contract on top of the Telegram Bot API.
Package telegram implements the gateway.Adapter contract on top of the Telegram Bot API.
mcp
Package mcp wires Model Context Protocol servers into carlos's tool registry.
Package mcp wires Model Context Protocol servers into carlos's tool registry.
mcp/ccimport
Package ccimport discovers Model Context Protocol servers that the user has already configured for Claude Code and maps them onto carlos's own mcp.ServerConfig shape so they can be offered for import.
Package ccimport discovers Model Context Protocol servers that the user has already configured for Claude Code and maps them onto carlos's own mcp.ServerConfig shape so they can be offered for import.
memory
Package memory - see schema.go for the doc comment that anchors the package.
Package memory - see schema.go for the doc comment that anchors the package.
miniyaml
Package miniyaml is a tiny YAML subset parser tailored to carlos's actual usage.
Package miniyaml is a tiny YAML subset parser tailored to carlos's actual usage.
notes
Package notes is the Obsidian-flavored markdown indexer that backs the notes_* tool family.
Package notes is the Obsidian-flavored markdown indexer that backs the notes_* tool family.
projectctx
Package projectctx discovers and loads per-project agent context files (AGENTS.md / CLAUDE.md and their `.agents/` `.claude/` namespaced variants) so the model sees a project's house rules without the user having to copy-paste them every session.
Package projectctx discovers and loads per-project agent context files (AGENTS.md / CLAUDE.md and their `.agents/` `.claude/` namespaced variants) so the model sees a project's house rules without the user having to copy-paste them every session.
providers/anthropic
Package anthropic implements a pure-Go streaming client for the Anthropic Messages API.
Package anthropic implements a pure-Go streaming client for the Anthropic Messages API.
providers/fake
Package fake is a deterministic provider used by Phase 1 preflight tests.
Package fake is a deterministic provider used by Phase 1 preflight tests.
providers/gemini
Package gemini implements a pure-Go streaming client for Google's Generative Language API via its OpenAI-compatible endpoint at https://generativelanguage.googleapis.com/v1beta/openai/.
Package gemini implements a pure-Go streaming client for Google's Generative Language API via its OpenAI-compatible endpoint at https://generativelanguage.googleapis.com/v1beta/openai/.
providers/oacompat
Package oacompat implements the OpenAI Chat Completions wire protocol shared by carlos's `openai` and `openrouter` providers.
Package oacompat implements the OpenAI Chat Completions wire protocol shared by carlos's `openai` and `openrouter` providers.
providers/ollama
Package ollama implements a pure-Go streaming client for the Ollama /api/chat endpoint.
Package ollama implements a pure-Go streaming client for the Ollama /api/chat endpoint.
providers/openai
Package openai implements a pure-Go streaming client for the OpenAI Chat Completions API.
Package openai implements a pure-Go streaming client for the OpenAI Chat Completions API.
providers/openrouter
Package openrouter implements a pure-Go streaming client for the OpenRouter chat-completions endpoint.
Package openrouter implements a pure-Go streaming client for the OpenRouter chat-completions endpoint.
research
induction.go - slice 11h.
induction.go - slice 11h.
sandbox
Package sandbox provides execution backends for carlos sub-agents.
Package sandbox provides execution backends for carlos sub-agents.
schedule
Package schedule owns carlos's scheduled-run vocabulary:
Package schedule owns carlos's scheduled-run vocabulary:
skills
curator.go - staleness sweep over the active skill library.
curator.go - staleness sweep over the active skill library.
skills/skillwire
metrics.go - skill-induction instrumentation.
metrics.go - skill-induction instrumentation.
theme
Package theme is the single source of truth for the colors every TUI surface (chat, manage, onboarding) renders with.
Package theme is the single source of truth for the colors every TUI surface (chat, manage, onboarding) renders with.
todo
Package todo is carlos's pluggable task/reminder layer.
Package todo is carlos's pluggable task/reminder layer.
tools
Package tools - HTTPRequestTool.
Package tools - HTTPRequestTool.
tui
tui/chat
activity_strip.go renders a run of consecutive tool-call entries as a single indented line ("activity strip") instead of the legacy bordered tool-card group.
activity_strip.go renders a run of consecutive tool-call entries as a single indented line ("activity strip") instead of the legacy bordered tool-card group.
tui/chatglue
Package chatglue wires the chat TUI to a real provider + tool loop.
Package chatglue wires the chat TUI to a real provider + tool loop.
tui/diff
Package diff renders unified-diff text into a lipgloss-styled string for the manage focus pane.
Package diff renders unified-diff text into a lipgloss-styled string for the manage focus pane.
tui/manage
Package manage is the bubbletea sub-agent supervision TUI - the marquee surface from SPEC § Manage mode.
Package manage is the bubbletea sub-agent supervision TUI - the marquee surface from SPEC § Manage mode.
tui/onboarding
Package onboarding owns the six-screen first-run flow and the portrait protocol cascade.
Package onboarding owns the six-screen first-run flow and the portrait protocol cascade.
tui/slash
Package slash owns carlos's TUI slash-command vocabulary.
Package slash owns carlos's TUI slash-command vocabulary.
tui/termscrub
Package termscrub strips terminal escape-sequence remnants that leak into TUI text-input fields.
Package termscrub strips terminal escape-sequence remnants that leak into TUI text-input fields.
usershell
Package usershell implements the `!`-prefix feature: shell commands the USER types in the chat composer, run in their own context (not the agent's sandboxed bash tool), with output added to the conversation as context for the next model turn.
Package usershell implements the `!`-prefix feature: shell commands the USER types in the chat composer, run in their own context (not the agent's sandboxed bash tool), with output added to the conversation as context for the next model turn.
web
Package web hosts the "carlos web" HTTP + SSE server: a localhost projection surface over the agent event log, mirroring what the chat TUI is to the same log.
Package web hosts the "carlos web" HTTP + SSE server: a localhost projection surface over the agent event log, mirroring what the chat TUI is to the same log.
workspace
Package workspace owns carlos's persistent workspace-trust model.
Package workspace owns carlos's persistent workspace-trust model.

Jump to

Keyboard shortcuts

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