Development
Blitz is an AI coding agent in Go, built on the Google Agent Development Kit (google.golang.org/adk/v2). This section is for people and agents changing the code (contributing is the short version); the architecture and shared packages explain how the pieces fit. Start with Where to pick up (where work stands and what’s next); the roadmap explains what was built and why, and Manual verification holds the checks that need a person.
A monorepo of independent apps over shared packages, one Go module. The rule: apps depend on pkg/, never on each other (tests may run the service through apps/service/servicetest); front ends (the CLI’s REPL, the desktop app) depend on the contract (pkg/api) and the client (pkg/client), never on the engine; only the service and the CLI’s --local mode link pkg/engine. Code private to one app lives under apps/<app>/internal/.
| Path | What |
|---|---|
apps/cli |
blitz, the CLI: flags, --dir, output modes, one-shot runs, doctor, config, workers, service install. Links no engine: attaches to the service (starting it when it isn’t running), or to a private blitzd for a run of its own (--local); doctor runs in blitzd |
apps/cli/internal/tui |
The REPL: commands, rendering, input, steering keys. Drives an api.Backend |
apps/service |
blitzd, the per-user service: internal/daemon runs it, internal/server has the Connect handlers over engine.Workspace (translation only) and the approval/question broker. servicetest runs it inside other packages’ tests |
apps/vscode |
The VS Code extension (TypeScript): the desktop app’s page in a view, a local proxy to the service, packed by tools/vsix |
apps/desktop/web |
The desktop page (React + TypeScript, pnpm, the generated Connect client in src/gen), shown by the Electron app, the tray’s page on Windows and the VS Code extension |
apps/electron |
The desktop app (spec_electron_038): its main process and preload (TypeScript), with the page and blitz-tray --serve as its host (pkg/pagehost); the Electron runtime comes from MODULE.bazel; appicon.png its icon; winexe (Go, run at build time) gives the Windows program that icon and its details; packaging/ its macOS app, Windows zip and Linux packages (the Arch one made by tools/pacman) and the macOS signing script |
apps/tray |
blitz-tray, the service in the system tray (fyne.io/systray: D-Bus StatusNotifierItem on Linux, Cocoa on macOS, Win32 on Windows): internal/tray has its behaviour (the menu for a status, the actions, the page’s /host/ calls), main.go draws it; shipped beside the app in the .deb and Blitz.app, and in the Windows zip, where it serves the desktop page (bazel run //apps/tray:blitz-tray_page tries that here) |
pkg/api |
The contract between front ends and the engine: Backend, and the values, events and errors that cross it. Depends only on config and images |
pkg/client |
Backend over the service’s API |
pkg/loginitem |
Starting the service at login and controlling it (launchd, systemd), and the tray (also the registry’s Run key on Windows), for the CLI, the desktop app and the tray |
pkg/pagehost |
The page’s calls to the program serving it (blitz-tray --serve for the Electron app, the tray’s page on Windows): the window’s preferences (~/.blitz/desktop.json), the service, installing the blitz command, the OS sandbox, updates |
pkg/pageserver |
The desktop page in front of the service: the proxy to its socket (the tray uses it, for the Electron app and on Windows), and the loopback server, behind a one-time link and a cookie, that serves the page to a browser on Windows |
pkg/fileview |
Showing files and folders in the system’s programs (file manager, viewer), never running one: for the desktop app and the tray |
pkg/socket |
Where the service listens (the per-user Unix socket), and how to reach it |
pkg/engine |
The program without a UI: engine.Open builds a Workspace (registries, tools, sessions, model, engine) implementing api.Backend with typed operations that return data and never print |
pkg/engine/runtime |
The engine over the ADK runner: models and providers, fallback, per-model settings, compaction, steering, side questions, usage and cost |
pkg/engine/tools |
Tools and their guardrails: workspace roots, approvals, command policy, OS sandbox, script sandbox (gVisor), skill scripts and their environments, web, MCP, hooks, checkpoints |
pkg/engine/session |
Saved sessions, snapshots, the ADK event store, transcript search |
pkg/engine/{skills,agents,commands,memory,workers,audit,breaker} |
Skill, agent and command definitions (built-ins embedded), memory files, workers, the audit log, the circuit breaker |
pkg/{config,i18n,images,legal,observability,redact,secrets,textutil} |
Shared by every app: configuration (.env.toml through modenv) and its editing, message catalogs (en-US, es, fr-CA, also read by the desktop page), images, the license texts every program shows, the diagnostic log and OpenTelemetry, secret redaction, API keys in the OS keychain, text helpers |
proto/blitz/v1 |
The service API as protos (buf.yaml, buf.gen.yaml at the root): SessionService (sessions, turns, steering, approvals), WorkspaceService (everything else api.Backend does), WorkerService; their Go code (messages and Connect, one package) is generated by Bazel, never committed |
build |
How things are built: code generators (Connect, protoc-gen-es), nogo and its analyzers (doccomment), macOS signing, version stamping |
tools |
Runnable checks and upkeep: check_deps, check_format.sh, license_headers, third_party_notices.sh (and its generator, notices), specs, tidy, buf, the gopls driver |
third_party |
Patches to dependencies |
docs |
This site: Hugo with the Geekdoc theme, built by Bazel (//docs:site) and published to GitHub Pages from main |
release |
The CLI release archives, every platform (spec_monorepo_028 MR-22) |
.github/workflows |
ci.yml (Bazel: every test with the race detector, sandbox and gVisor checks, the dependency rules, formatting, govulncheck, protos, on macOS and Linux; release archives must be identical on both), release.yml (Bazel-built archives, SBOMs, cosign, the desktop packages) |
The short names (//:blitz, //:desktop, //:build-all, …) are aliases in the root BUILD.bazel, which lists them all.
bazel build //... # everything; binaries in bazel-bin/apps/{cli,service,tray}
bazel build //:build-all # this machine's programs and desktop package (//:build-all-linux, //:build-all-mac)
bazel test //... # every test (cached: only what changed runs again)
bazel run //:blitz -- doctor --online # the CLI; //:blitzd the service
bazel run //tools:check_deps # apps depend on pkg/ only; front ends never on the engine
bazel run //:gazelle # after adding a Go package or an import: update the BUILD files
bazel test --config=race //... # the Go tests with the race detector, as CI runs them
bazel run //tools:tidy # go mod tidy, bazel mod tidy, Gazelle (after changing dependencies)
tools/check_format.sh # gofmt, BUILD files as Gazelle writes them, license headers
bazel run //tools:license_headers # add the Apache header to new files
tools/third_party_notices.sh # regenerate THIRD_PARTY_NOTICES after changing dependencies
bazel run //tools/specs # the specs' index and the paths they name
bazel run //:docs # this site at http://localhost:1313
tools/coverage.sh # test coverage of apps/ and pkg/; fails below tools/coverage/floor.txt
bazel test //apps/desktop/web:coverage # the desktop page's line and branch coverage; fails below vite.config.ts's thresholds
bazel run //tools/codeowners # .github/CODEOWNERS from OWNERS.txt
bazel run //:desktop # the desktop app (Electron, spec_electron_038) on this machine's service
bazel run //:desktop-web # the desktop page in a browser (?fake: no service needed)
bazel build //:mac-app # the desktop app for this Mac; //:deb, //:pacman, //:desktop-tar on Linux; //:windows-zip anywhere
bazel build //:archives # the CLI release archives, every platform
BLITZ_TELEMETRY=1 BLITZ_LOG_LEVEL=debug bazel run //apps/cli:blitz # traces to http://localhost:4318; logs in ~/.blitz/logs
bazel test //pkg/engine/tools:tools_test --test_env=BLITZ_PYENV_TESTS=1 --test_filter='PyEnv|InstallsPackages' # real Python environments (network)
Everything goes through Bazel; host go, pnpm or buf commands are a last resort (bazel run @rules_go//go -- … is the go command). Plain go build and go test don’t work anyway: proto/blitz/v1 exists only in the build. Editors: point gopls at Bazel with GOPACKAGESDRIVER=$PWD/tools/gopackagesdriver.sh; for the page’s generated code, bazel build //apps/desktop/web:api_ts once. Static analysis (vet, staticcheck) runs in every compile; silence one finding with //nolint:<check>.
This site is in docs/: Markdown pages in docs/content/, built by Hugo with the Geekdoc theme through Bazel (bazel build //docs:site, which //... leaves out; bazel run //:docs previews it). Link pages by their files (../guide/safety.md); a link to a missing page fails the build.
- Diagrams are Mermaid: a fenced code block with the language
mermaid(flowcharts, sequence, class and state diagrams, …) renders as a diagram, in both themes. Geekdoc’s code-block render hook loads Mermaid only on pages that use it. - The API reference (
architecture/api/) is generated, never written://docs:apiruns proto-gen-md-diagrams (ago.modtool, built by Bazel) overproto/blitz/v1, anddocs/api_pages.shmakes its Markdown into pages. A new proto file needs adding to the list indocs/BUILD.bazel; better comments in the protos make better pages. - Versions. The guide describes Blitz as it is, not how it changed. It names a Blitz version only as “since vN.N.N” (what an older release lacks) or “deprecated since vN.N.N”; what changed in a release goes on its page in
releases/. A snippet that installs or uses a release names it with{{< release >}}, which the site fills with the latest published release (docs/layouts/shortcodes/release.html) and the app’s copy with its own version, so nothing needs bumping at a release. Other projects’ versions (a dependency, an exampleplugin.toml) are written as they are.
- One commit per feature, with a message explaining why.
- New user-facing strings go into all three catalogs in
pkg/i18n/locales/(en-US,es,fr-CA);i18n_lint_test.goenforces this. - A bug fix comes with a test that was confirmed to fail without the fix (temporarily revert, run, restore).
- Go tests use Testify:
requirefor what the rest of the test depends on (setup, a value it goes on to use),assertfor the checks; one assertion per fact, nota || b; the specific assertion (ErrorIs,Contains,Len,FileExists), notTrue(x == y); messages add what the values don’t say (which case, what it means) and never restate got and want. Table-driven loops run each case as a subtest,t.Run(name, …).t.Fatalandt.Errorstay for control flow: aselect’s timeout, an unexpected event, a helper that found nothing. - Each feature updates this site’s pages for it (the product and guide pages, its spec), a roadmap item, and a manual verification section.
- Real-terminal behavior was checked with
scriptagainst a local fake provider. Answer the line editor’s cursor-position query (ESC[6n) withESC[1;1Rin the scripted input, or it waits forever. - gVisor code only builds on Linux (
//go:build linux, stub elsewhere); check it from macOS withbazel build --platforms=@rules_go//go/toolchain:linux_arm64 //pkg/engine/tools. Its tests needrunsc(RUNSC_PATH); CI fails if they’re skipped. - License: Apache 2.0. Every source file starts with the Apache header (
bazel run //tools:license_headersadds it; CI checks it).NOTICEcredits Code Puppy, the Python project Blitz was ported from (MIT). - Every package and exported name has a doc comment; the
doccommentanalyzer (Go),doc.lint.test.ts(the page) and buf’s comment rules (protos) enforce it.