AI utility to build and use across different context. Local Transcriptions, composable, pipe friendly. Uses XDG and POSIX specifications. Designed with vim and coreutils.
  • Rust 82.1%
  • Shell 17.9%
Find a file
Andrew Briscoe fab04dc8fe
docs: add README with usage, configuration, and development workflow
Introduce a comprehensive 'README.md' for 'terminal-ai' covering the tool's purpose,
pipeline-safe output model, transcript architecture, and message threading design.

Document:
- build commands and runtime dependencies
- prompt resolution order and pipeline-oriented usage
- backend switching via 'TAI_BASE_URL' and 'OPENAI_API_KEY'
- output control through '-o' tokens
- transcript storage layout and inspection examples
- configuration precedence, core env variables, and CLI flag mappings
- development workflows for commit message generation and AI-assisted fixups

This adds initial project-facing documentation to improve onboarding, discoverability,
and day-to-day usage.
2026-04-17 23:04:35 +08:00
bin feat: add tai-today transcript helper and install command 2026-04-14 01:57:17 +08:00
docs docs: expand TOOLS guide with conventions and tool reference 2026-04-17 23:04:35 +08:00
src feat: add Rust CLI implementation for pipeline-safe terminal AI client 2026-04-17 23:04:34 +08:00
tools chore(tools): improve generated commit prompt formatting 2026-04-17 23:04:34 +08:00
utils feat: add tai-today transcript helper and install command 2026-04-14 01:57:17 +08:00
.gitignore feat(tools): add fix and generated report helper scripts 2026-04-17 23:04:27 +08:00
Cargo.lock feat: add Rust CLI implementation for pipeline-safe terminal AI client 2026-04-17 23:04:34 +08:00
Cargo.toml feat: add Rust CLI implementation for pipeline-safe terminal AI client 2026-04-17 23:04:34 +08:00
install.sh feat: add tai-today transcript helper and install command 2026-04-14 01:57:17 +08:00
README.md docs: add README with usage, configuration, and development workflow 2026-04-17 23:04:35 +08:00

terminal-ai

A pipeline-safe terminal AI client that records conversations as structured Markdown transcripts. Each exchange is written independently to three output channels, each with its own rendering policy, so the same binary pipes cleanly between shell tools while archiving full metadata for later replay.

cargo build --release

Runtime dependencies: curl (HTTP), sha256sum (message IDs), date (timestamps). All standard on Linux.


What it does

Three output channels

Every turn is rendered independently to each enabled sink:

Channel Purpose Default format
stdout Clean AI response for pipelines Content only
transcript Append-only archival log Header + indent + ---
artifact Explicit per-run output file Configurable

stderr is a fourth channel reserved for metadata, errors, and interactive prompts. It never carries conversation content.

# Count words in a response without contamination
terminal-ai "list ten unix commands" | wc -w

# Full transcript format on stdout for inspection
TAI_STDOUT_HEADER=1 TAI_STDOUT_INDENT=1 TAI_STDOUT_SEP=1 terminal-ai --dry-run "test"

Message IDs and conversation threading

Every turn is identified by a deterministic hash of who spoke and when, paired with the ID of the turn it responds to. This forms a directed graph across the transcript without a database.

SpeechID   := HASH8(Speaker, Timestamp)
AudienceID := parent SpeechID  |  HASH8("NULL") for roots
MessageID  := "<SpeechID>-<AudienceID>"

--parent-id attaches a new turn to an existing prior speech, enabling branching and multi-pass workflows across separate invocations.

See docs/concepts/message-ids.md for the full graph model.


Build

cargo build --release          # production binary
cargo build                    # debug binary (used by generate-commit-msg.sh for dogfooding)

Cargo workspace: Cargo.toml at repo root. Single binary: terminal-ai.


Use

Prompt sources

Prompts are resolved in order:

  1. Trailing CLI words: terminal-ai "explain monads"
  2. -- separator: terminal-ai -- explain monads
  3. Piped stdin: echo "explain monads" | terminal-ai
  4. Interactive: invoked with no arguments in a TTY

Multi-line input via xargs -0:

cat context.txt | xargs -0 terminal-ai "summarise this"

Pipeline usage

stdout is content-only by default. Pipe freely:

terminal-ai "list ten unix commands" | fzf
terminal-ai "generate a config" | tee ./out.conf
terminal-ai "question" | head -n1   # exits cleanly with code 0

Backend switching

Any OpenAI-compatible endpoint. Bearer header is sent only when OPENAI_API_KEY is set:

TAI_BASE_URL=http://localhost:8080                           terminal-ai "…"   # llama.cpp, keyless
TAI_BASE_URL=https://api.deepseek.com OPENAI_API_KEY=sk-… terminal-ai "…"
TAI_BASE_URL=https://api.openai.com   OPENAI_API_KEY=sk-… terminal-ai "…"

Transcripts record the model name reported by the backend, so switching backends is attributable without any other change.

Output control

Fine-grained suppression and formatting via -o:

terminal-ai -o NO_USER_TRANSCRIPT "question"      # suppress user turn from log
terminal-ai -o CONTENT_ONLY_STDOUT "question"     # no header/sep on stdout
terminal-ai -o NO_METADATA "question"             # drop usage metadata from all channels
terminal-ai -o NO_AI_STDOUT,NO_USER_TRANSCRIPT …  # combined, comma-separated

Full token grammar: docs/CONFIGURATION.md#output-control.

Transcript inspection

Transcripts are append-only dated Markdown files under $XDG_DATA_HOME/terminal-ai/transcripts/default/.

grep '^\[' ~/.local/share/terminal-ai/transcripts/default/$(date --rfc-3339=date).md

Configure

Resolution order, highest to lowest:

CLI flag  →  process env  →  TAI_ENV_FILE / --env-file  →  config.env  →  .default.env  →  default

Core variables:

Variable CLI flag Default Purpose
TAI_MODEL -m, --model gpt-5.4 Model name
TAI_BASE_URL -b, --base-url https://api.openai.com API base URL
OPENAI_API_KEY Bearer token
TAI_TRANSCRIPT_DIR --transcript-dir ~/.local/share/…/default Transcript directory
TAI_OUTPUT_FILE -O, --output-file Artifact output path
TAI_SPEAKER_USER USER User turn label
TAI_SPEAKER_AI model name from response AI turn label override
TAI_NO_TRANSCRIPT --no-transcript false Disable transcript writing

Full reference: docs/CONFIGURATION.md.


Develop

Commit workflow

  1. Stage a self-contained unit with GitUI
  2. ./tools/generate-commit-msg.sh — pipes git diff --cached to debug binary
  3. Review with ./tools/view-last-commit-msg.sh
  4. Commit git commit -m $(./tools/view-last-commit-msg.sh

Generated files land in .generated/ with UUIDv7 names. Never commit .generated/.

LLM-assisted fixup

./tools/fix.sh            # clippy → AI → .generated/fix-<uuid>
./tools/fix-tools.sh      # shellcheck on tools/ → AI → .generated/shellcheck-<uuid>
./tools/show-fix.sh       # view latest fix report (mdless)
./tools/show-fix.sh --pipe  # or pipe it

Both fixup tools use the release binary. The commit message tool uses the debug binary to surface regressions early.