This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ai is a Go CLI that turns a natural-language request into a shell command via OpenRouter, then shows it in a Bubble Tea TUI for review before executing it through $SHELL -c. It also has a chat mode (-c) that returns a free-form answer plus an optional suggested command.
go build -o ai . # build the single binary
go run . <request> # run from source
go test ./... # run tests (no test files yet — runs cleanly)
go vet ./... # vet
gofmt -l . # list unformatted filesThere is no Makefile, lint config, or CI yet.
main.go is the Cobra entry point. It loads config, resolves the API key (env → 1Password op_ref → plaintext api_key), runs the first-time TUI wizard if either key or model is missing, then dispatches to handleCommand (default) or handleChat (-c). Both handlers have two paths: a non-TTY path (used with --print, --yes, or piped output) that bypasses the TUI, and a TTY path that drives ui.RunReview.
Internal packages — each owns one concern, no cross-imports between siblings except via runner → none and exec → history/ui:
internal/config— YAML at~/.config/ai/config.yaml(Configstruct,Load/Save/Path), the first-run Bubble Tea wizard (wizard.go,picker_io.go) that picks API-key source and fuzzy-searches the live OpenRouter model catalog, andResolveOpRefwhich shells out toop readfor 1Password references.RedactSecretsEnabled()defaults true when the YAML field is absent.internal/runner— the OpenRouter call. Two modes (modeCommand,modeChat) with strict JSON schemas (commandSchema,chatSchema) so the model returns structured{command, explanation}or{answer, suggested_command}.Options.RedactSecretsflows through tocontext.go, which builds the system-prompt context: OS/shell/hostname/cwd fromsysinfo.go, plus a top-level directory listing (.gitignore-aware inside a git repo) andgit statussummary. Sensitive filenames (dotenv, PEM, SSH keys, cloud creds, etc.) are replaced with[redacted]— the redaction list lives incontext.goand the README documents it.internal/ui— Bubble Tea + Lipgloss.RunReviewis the unified review TUI used by both command and chat modes; it takes a generator callback so the spinner and the API call run concurrently. Catppuccin Mocha palette instyle.go.internal/exec— runs the chosen command via$SHELL -c(falls back to/bin/sh), inheriting stdio. Callshistory.Appendbefore exec.internal/history— best-effort append to$HISTFILE(or~/.zsh_history) in zsh extended-history format. No-op for non-zsh shells. Errors are surfaced as a hint, never fatal.
--printand--yesare mutually exclusive and are required when stdout/stderr is not a TTY (CI, pipes); without one of them the CLI errors instead of running silently.- Piped stdin is read in
readPipedStdinand passed asOptions.StdinContextso the model sees it as additional context. The TUI still works because Bubble Tea reads from/dev/tty, not stdin. - Default model constant is
defaultModelinmain.go(currentlyanthropic/claude-sonnet-4-5); the README example usesanthropic/claude-haiku-4.5. Treat the constant as the source of truth. - The OpenRouter client is
github.com/hra42/openrouter-go(a separate project by the same author). Structured outputs use itsJSONSchemawithStrict: true. - Go module targets
go 1.26.2(very recent — older toolchains will reject the build).