A docs-driven coding harness: a coding agent that works from a spec.md and a
backlog of tasks. ycc is a single binary that is client, TUI, and daemon in
one.
- With no subcommand it launches the interactive TUI (the home menu).
- Subcommands (
start,attach,list,cost, …) drive sessions from the command line and stream their event logs. ycc daemonruns the explicit, persistent, foreground service.
See spec.md for the full design, docs/cli.md for
the complete command reference, and CONTRIBUTING.md for the
project's lean engineering standards.
The interactive TUI streaming a work session (user turn, model turns, collapsed
Read/Edit/Bash tool calls, and the final report). Rendered deterministically by
the built-in snapshot renderer — regenerate with YCC_README_SCREENSHOT_DIR=docs go test ./internal/tui -run TestGenerateReadmeScreenshot.
go install github.com/whyrusleeping/ycc/cmd/ycc@latest # install to $GOBIN
go build -o ycc ./cmd/ycc # or build in-treeycc ships completion scripts for bash, zsh, and fish (run
ycc completion --help for Powershell and details). Source the script for your
shell — ycc attach/ycc stop then complete against live session ids and
--project completes registered project names when a daemon is reachable:
source <(ycc completion bash) # ~/.bashrc
source <(ycc completion zsh) # ~/.zshrc
ycc completion fish > ~/.config/fish/completions/ycc.fish# Interactive TUI (runs a one-shot in-process daemon, torn down on exit):
ycc
# Start a session from the CLI and stream it; type lines to prod the agent:
ycc start "add a hello.txt"
# Re-attach to a running session, replaying its log from the start:
ycc attach s_abc123 --from 0
# List sessions / modes:
ycc list
ycc modes
# Usage & cost breakdown (by backlog task, by default):
ycc cost --by task --since 2026-06-01Every command is self-documenting — run ycc --help for the command list and
ycc <command> --help for any command's flags and arguments.
Persistence is opt-in:
- Default (
ycc,ycc start …): if a persistent local daemon is already running, attach to it; otherwise start the daemon in-process on an ephemeral loopback address tied to this process. It is torn down when the process exits — closingyccends any in-flight agent work. ycc --background: spawn a detached, persistent daemon and attach to it, so work keeps running after the client exits.ycc daemon: run the persistent daemon explicitly in the foreground.ycc --addr <URL>: attach to a remote/explicit daemon.
ycc looks for a TOML config (ycc.toml) describing model providers and
workflow roles. It is discovered in this order:
./ycc.toml(the workspace directory)$XDG_CONFIG_HOME/ycc/ycc.toml(typically~/.config/ycc/ycc.toml)
Launching the TUI with no usable config runs a first-run setup wizard that writes
~/.config/ycc/ycc.toml. You can also pass an explicit file with --config.
Projects with normative design documents beyond spec.md declare them in the committed
.ycc/config.toml project config:
spec_path = "spec.md"
doc_globs = ["docs/*.md", "docs/design/*.md"]Keep runtime state ignored while making that one file committable with .gitignore rules such
as .ycc/* followed by !.ycc/config.toml. Use narrow globs for normative documents; do not
include memory.md, backlog/, generated files, or advisory reports. ycc spec-check scans the
entry point and every matching file.
Never paste credentials into a session prompt or an ask_user answer: those
strings are durable model history. Use the local secret-entry command instead.
Interactive entry disables terminal echo; stdin is supported for password-manager
pipes, and values are never accepted in argv:
ycc token set ANTHROPIC_API_KEY
ycc token set EXA_API_KEY
ycc token list
ycc token rm EXA_API_KEYModel backend credentials named by key_env are resolved from the daemon
environment first and then this machine-local store. Stored values are not added
to tool process environments and there is no model-visible secret lookup tool.
The only model-visible named-secret consumers are currently the Exa tools. Each
stored-secret use requires a separate authorization scoped to the canonical
workspace and one tool invocation:
ycc token authorize EXA_API_KEY --tool web_search --workspace /path/to/project
ycc token authorize EXA_API_KEY --tool fetch_page --workspace /path/to/project
ycc token authorizations # reference-only grant/use audit; never valuesAn EXA_API_KEY deliberately placed in the daemon environment is already
operator-authorized for that daemon process; use the stored-secret workflow when
single-use tool authorization is wanted.
YCC refuses high-confidence credential-shaped prompts and question answers before recording them, and exports and routine CLI event displays redact exact stored values plus a few obvious credential shapes with a visible warning. These are guardrails, not perfect secret detection: arbitrary prompts, files, shell commands, tool output, web content, screenshots, or unfamiliar credential formats can still leak values. YCC does not regex-rewrite durable events because doing so could silently corrupt model replay.
If a credential was previously entered into a session, rotate/revoke it at the
provider first. Then choose deliberately: retain the owner-only session log for
replay, delete the entire affected .ycc/sessions/<session-id> directory while the
daemon is stopped (losing that session's replay/export), or make a separately
redacted copy for sharing. Hand-editing events.jsonl trades away append-only
history integrity and may break replay; YCC never edits or deletes old logs
automatically.
| Variable | Used for |
|---|---|
ANTHROPIC_API_KEY |
default LLM backend key (the key_env configured per model) |
EXA_API_KEY |
the web_search / fetch_page tools (Exa) |
YCC_TOKEN |
bearer token for --addr / ycc daemon auth |
CI uses Go 1.26.8 on Linux and Xcode 16.4 on macOS. Run the corresponding local checks before submitting a change (the Swift checks require macOS):
# Go formatting, static checks, uncached tests, PTY TUI tests, race detector, and docs drift.
test -z "$(git ls-files '*.go' | xargs gofmt -l)"
go vet ./...
go test -count=1 ./...
go test -count=1 -v ./internal/e2e # every TestE2E test must PASS, not SKIP
go test -race -count=1 ./...
go run ./cmd/ycc spec-check
# Vulnerability scan (the Go patch version is part of the result).
go install golang.org/x/vuln/cmd/[email protected]
govulncheck ./...
# Protobuf reproducibility; the remote Swift plugins are pinned in buf.gen.swift.yaml.
go install github.com/bufbuild/buf/cmd/[email protected]
go install google.golang.org/protobuf/cmd/[email protected]
go install connectrpc.com/connect/cmd/[email protected]
buf generate
buf generate --template buf.gen.swift.yaml
git diff --exit-code -- proto/ycc/v1 clients/ios/YccKit/Sources/YccProto
test -z "$(git status --porcelain -- proto/ycc/v1 clients/ios/YccKit/Sources/YccProto)"
# Swift package tests and generated app build (XcodeGen 2.44.1).
swift test --package-path clients/ios/YccKit
(cd clients/ios && xcodegen generate)
xcodebuild -project clients/ios/Ycc.xcodeproj -scheme Ycc \
-destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO buildThe test harnesses use local stubs and temporary credentials; these checks do not need provider API keys. Install the exact Buf, generator, govulncheck, and XcodeGen versions shown above to match CI.
