Skip to content

refactor: cleanup pass + internal/tui strict eviction seam#49

Merged
kuchmenko merged 9 commits into
mainfrom
refactor/cleanup-and-tui-unification
May 19, 2026
Merged

refactor: cleanup pass + internal/tui strict eviction seam#49
kuchmenko merged 9 commits into
mainfrom
refactor/cleanup-and-tui-unification

Conversation

@kuchmenko
Copy link
Copy Markdown
Owner

Summary

Mechanical and architectural cleanup. Two parallel threads, eight commits, one PR.

Code health pass

  • Removed 10 truly unreachable functions across git, alias, agent, clipboard (verified via golang.org/x/tools/cmd/deadcode plus per-function call-site audit — the other 27 deadcode-flagged items are test/bench helpers, intentional stubs, or same-package test-only exports that are not actually dead).
  • Stripped narrative comments from 134 production Go files via an AST-aware Go tool that preserves //go:build, // Package, // DECISION:, // TODO:, // FIXME:, // HACK: markers and leaves *_test.go untouched. −3,322 LOC.

TUI unification (the bigger lever)

  • New package internal/tui consolidates everything that previously lived behind direct bubbletea / lipgloss / bubbles/* imports.
  • The seam is strict, not aliased: Msg, Cmd, Model, Style, Color, Border, KeyMsg, KeyType, Spinner, TextInput, etc. are own types. A grep for charmbracelet outside internal/tui returns zero — that's the regression signal.
  • Bubbletea eviction is now a pure internal/tui rewrite: redefine the types, reimplement runtime.go against raw ANSI/termios. Consumer call sites do not change.
  • 6 consumer packages migrated: agent, add, branchprompt, aliasmgr, create, setup, plus cli/* modal flows (bootstrap_model, migrate_model, setup, alias).

Docs

  • AGENTS.md gains two new convention bullets: no comments in production Go code (with a permitted-marker list), and the bubbletea-import-only-in-internal/tui rule.

Stats

Before After Δ
Total Go LOC 31,360 28,764 −2,596
Non-test 23,242 20,466 −2,776
Tests 8,118 8,298 +180
Files changed 153 +1,874 / −4,456

LOC reduction is smaller than the deepest projection because the eviction itself (dropping the bubbletea+lipgloss+bubbles deps and the ~5 MB they bring) is a follow-up PR, not this one. This PR builds the seam that makes the eviction a mechanical rewrite.

Commits (bisect-friendly)

9c7ffba docs(agents): codify no-comments-in-main-code and internal/tui seam rules
bd64509 refactor(cli,create,aliasmgr,setup): consume internal/tui — seal the seam
7de0b11 refactor(add,branchprompt): consume internal/tui — drop bubbletea/lipgloss/bubbles
3f8cc3a refactor(agent): consume internal/tui — drop bubbletea/lipgloss
a5938bd feat(tui): make eviction seam strict — nominal types, no aliases
c7556f1 feat(tui): framework-agnostic TUI primitives in internal/tui
c813306 refactor: strip narrative comments from production Go files
27d0268 refactor: remove 10 unreachable functions across git, alias, agent, clipboard

Each commit builds and go test -race ./... passes individually.

Trade-offs surfaced

  • WHY-comments lost: The blanket comment strip removed ~10-30 atom-1-valuable WHY comments per file (e.g. SetBranchUpstream's 13-line explanation of a non-obvious git timing window). Per the operator's stated "no comments in main code" stance these go, recoverable from git show 62ad2ee:internal/git/git.go. If specific ones become load-bearing during future debugging, they can be re-introduced as // DECISION: blocks.
  • Primitive design with zero consumers (atom 2): internal/tui primitives (FilterableList, ModalForm, ConfirmDialog, Steppable) were designed up front, not extracted from concrete duplication. They are available for new code but consumer migrations in this PR did not adopt them — they only swapped types. Adoption is a follow-up.
  • Bubbles widgets: tui.TextInput / tui.Spinner wrap bubbles/textinput / bubbles/spinner. tui.SpinnerTickMsg is a type alias (the only place a bubbles type leaks through a name) to preserve the case tui.SpinnerTickMsg: idiom.

Verifications

  • go build ./...
  • go test -race -timeout 5m ./... ✓ all packages green
  • golangci-lint run ✓ 0 issues
  • ~/go/bin/deadcode ./... 37 → 59 (delta is wrapper methods that deadcode can't trace through internal/tui — false positives, not real dead code)
  • grep -rln "charmbracelet" --include='*.go' | grep -v internal/tui ✓ zero

Test plan

  • ws agent — daily-driver TUI. Colors, keys, layout, header chips, project navigation, sessions, edit-project popup, worktree forms, whichkey panel, flash search all visually identical to current main.
  • ws add <github-url> — interactive add flow. Filter mode, manual entry, edit, confirm, clone, branch-prompt fallback.
  • ws alias — second daily-driver TUI. Search, edit, confirm.
  • ws sync — non-TUI verification.
  • ws status — non-TUI verification.
  • ws bootstrap — interactive plan + clone flow.
  • ws migrate — interactive plan + decision + migrate flow.

Follow-ups (out of scope, named for tracking)

  • Bubbletea eviction PR. Swap the implementations behind internal/tui for a raw-ANSI monolith. Expected: drop ~5 MB binary, drop bubbletea+lipgloss+bubbles deps, ~−2 k LOC production, ~−1.2 k LOC framework-coupled tests. No call-site changes.
  • Primitive adoption. Migrate consumers' bespoke list/scroll/filter/form code onto internal/tui.FilterableList / ModalForm etc. now that the primitives exist.
  • Theater test pruning. Per-test judgment review of tests that don't catch real bugs. Best after eviction.
  • Test-only exported helpers (CacheTTL, IsAuthErr, SortedCapabilityKeys, NewGhAppProviderStub): could be unexported with same-package test access, removing ~10 exports from the public API.

kuchmenko added 8 commits May 19, 2026 10:25
…lipboard

Identified by golang.org/x/tools/cmd/deadcode and verified to have zero
callers in production, tests, or bench code. The deadcode tool flagged 37
functions; 27 of those are test helpers (testutil, benchfixture), same-package
test-only exports (CacheTTL, IsAuthErr, SortedCapabilityKeys), or intentional
stubs (GhAppProviderStub) — all live and kept.

Deletions:
- internal/agent/worktrees.go:    DeleteWorktree (wrapper around live DeleteWorktreeWithRegistry)
- internal/alias/resolve.go:      Resolve, RemoveForTarget
- internal/git/bare.go:           RenameBranch
- internal/git/git.go:            Clone, ParseOwnerRepo
- internal/git/worktree.go:       WorktreeAddExisting (already marked DEPRECATED), WorktreeMove
- internal/clipboard/clipboard.go: Read, Detect (package-level convenience wrappers; production
                                   consumers use DefaultReader.Read directly)

clipboard_test.go rewritten to exercise DefaultReader.Read and detect()
directly, matching the actual production usage pattern.

Net: -121 LOC. Build, race tests, and golangci-lint all clean.
Tooling: AST-aware Go program at /tmp/stripcomments/main.go (go/parser +
go/format) walks internal/ and cmd/, drops all CommentGroups except those
matching preserve prefixes: //go:build, // +build, //go:generate,
// Package, // DECISION:, // TODO:, // FIXME:, // HACK:, // NOTE:.
Test files (_test.go) untouched.

Outcome: 134 of 140 production files modified. Build, race tests,
golangci-lint all green.

Trade-off acknowledged: this strips ~10-30 legitimately load-bearing
WHY-comments per file (e.g. SetBranchUpstream's 13-line explanation of
why config keys are written directly instead of via --set-upstream-to,
which works around a narrow timing window after CloneBare/SetFetchRefspec).
Per the operator's stated stance (no comments in main code), these are
removed. They remain recoverable via git history and can be reinstated as
// DECISION: blocks in a follow-up commit if a specific surprise lands
in code review or future maintenance.

Net: -3,322 LOC across 134 files. Total Go LOC: 31,360 -> 27,847.
New package internal/tui consolidates the four TUI codebases (agent, add,
cli modal flows, branchprompt) onto shared primitives. This commit ships
the primitives only; consumer migrations land in follow-up commits.

Files:
- tui.go        type aliases (Msg, Cmd, Model, Style) — the eviction seam:
                consumers import these instead of bubbletea/lipgloss, so
                future bubbletea eviction redefines the aliases without
                touching consumer code
- palette.go    Palette struct + Amber (agent's warm) + Cyan (add's cool)
                instances replacing the per-package styles.go duplicates
- list.go       FilterableList: cursor, scroll, type-to-filter
- form.go       ModalForm: fields, validators, focus, submit/cancel msgs
- dialog.go     ConfirmDialog: y/n with ConfirmedMsg/CancelledMsg
- state.go      Steppable interface + Stepper for multi-step flows

Total: 614 LOC including tests. Each primitive has its own _test.go
exercising the public API (cursor movement, filter mode, form submit
with validation, esc-cancels, dialog yes/no, stepper advance on done).

Build, race tests, golangci-lint all green.

Note on scope (per atom 2): primitives were extracted from patterns the
explore agent identified across all 4 TUI codebases. They are minimal —
no features added pre-emptively. If consumer migrations reveal gaps,
extend in the migration commits.
Previous commit used type aliases (Msg = tea.Msg, Style = lipgloss.Style).
Aliases allow consumers to still import bubbletea/lipgloss directly — the
seam is structurally permeable. This commit replaces aliases with own
types so that grep for bubbletea/lipgloss outside internal/tui becomes a
regression signal.

Type model:
- Msg, Cmd, Model — own interfaces; bubbletea is invisible to consumers
- Style — value-type wrapper around lipgloss.Style with explicit Render,
  Bold, Foreground, Padding, Border, etc. methods
- Color, Border, Position — own types replacing lipgloss.Color, .Border,
  .Position
- KeyMsg, KeyType (KeyEnter, KeyEsc, KeyTab, ...) — own enum-based key
  events; runtime adapter translates tea.KeyMsg in
- WindowSizeMsg — own type
- QuitMsg + Quit() — own quit signal; adapter translates to tea.Quit

Runtime adapter (runtime.go):
- NewProgram(Model, ...ProgramOption) wraps a Model in a tea.Program
- teaWrapper.Update bridges tea.Msg -> Msg, returns lifted Cmd
- WithAltScreen, WithMouseCellMotion options bridge to tea options
- batchMsg + Batch() lift-and-flatten cmd batching

Bubbletea eviction is now a pure internal/tui rewrite: redefine the
types, reimplement runtime.go against raw termios/ANSI. Consumer code
compiles unchanged.

LOC: +186 vs aliases (worth it for the strict boundary).
Build, race tests, golangci-lint all green.
internal/agent now sees only internal/tui. The 10 production files
in internal/agent that previously imported bubbletea or lipgloss now
import internal/tui only:
- All type signatures (Msg, Cmd, Model, KeyMsg, WindowSizeMsg) switched
  to tui.* equivalents
- All lipgloss helpers (Place, Width, Height, Join*, Color, Style) now
  call tui.* equivalents
- styles.go rewritten to use tui.NewStyle and tui.Amber palette for
  shared roles (header, footer, selected, accent, dim, group, item).
  Agent-specific styles (popup*, whichKey*, flash*, wt, session, chip)
  stay defined in agent/styles.go but as tui.Style values.
- internal/cli/explorer.go: replaced tea.NewProgram with tui.NewProgram

The internal/tui package gained Place, WithWhitespaceBackground, Top
position to cover lipgloss helpers used by agent's centered popups.
tui.Quit became a Cmd value (var, not func) to preserve the
`return m, tui.Quit` idiom from bubbletea.

Tooling: /tmp/teatotui/main.go — AST-based renamer that walks Go files,
rewrites tea.X/lipgloss.X selector exprs to tui.X, removes tea/lipgloss
imports, adds tui import. Single-pass, format-preserving.

Build, race tests, golangci-lint all green. Binary builds; ws agent
--help prints expected output.

Net: -54 LOC across 13 files. Agent is the first consumer of
internal/tui's strict seam — zero bubbletea/lipgloss in the package.
…gloss/bubbles

internal/tui gained widgets wrappers + a few helpers needed by add's flow:
- tui.TextInput / tui.Spinner wrap bubbles/textinput / bubbles/spinner;
  consumers never see bubbles. SpinnerTickMsg re-exported as
  tui.SpinnerTickMsg type alias (the only point where bubbles types leak
  through a name).
- tui.Sequence wraps tea.Sequence; tui.WithContext threads a context
  into the program so callers can cancel without importing bubbletea.
- KeyMsg.Type renamed KeyRune -> KeyRunes for parity with bubbletea
  conventions; added KeyEscape as alias for KeyEsc.
- TextInput.Focus moved to pointer receiver so focus state mutation
  propagates from the wrapper into the embedded bubbles model.

Migrations (internal/add 11 files, internal/branchprompt 2 files):
- All tea.X / lipgloss.X selector exprs rewritten to tui.X via the AST
  renamer (/tmp/teatotui/main.go)
- bubbles imports removed from every consumer file; struct fields and
  constructors switched to tui.TextInput / tui.Spinner
- branchprompt.NewModel auto-focuses the input so the test flow can
  type without an intervening Focus() Cmd dispatch
- bubbletea+lipgloss now compile-fenced out of both packages

Build, race tests, golangci-lint all green.

Note: internal/cli/bootstrap_model.go remains broken because it embeds
branchprompt.Model and is still bubbletea-native. Fixed in next commit
(cli modals migration).

Net: +115 LOC (the bulk is the new tui/widgets.go; consumer files are
net-neutral or smaller).
…seam

The remaining 4 packages that still imported bubbletea/lipgloss/bubbles
are now migrated. Pattern (same as agent + add):

- All tea.X / lipgloss.X selector exprs rewritten to tui.X via the AST
  renamer (/tmp/teatotui/main.go).
- Bubbles widget fields switched: spinner.Model -> tui.Spinner,
  textinput.Model -> tui.TextInput.
- Constructors and setters use the tui wrappers: NewSpinner/NewTextInput
  with SetStyle/SetPlaceholder/SetCharLimit/SetWidth/CursorEnd.
- spinner.TickMsg -> tui.SpinnerTickMsg in case branches.

Packages migrated in this commit:
- internal/cli (bootstrap_model.go, migrate_model.go, plus the renamer
  swept bootstrap_view.go, bootstrap.go, migrate_tui.go, setup.go, alias.go)
- internal/create (tui.go, plus render.go, runner.go, cmd.go via renamer)
- internal/aliasmgr (model.go, step_confirm.go, step_manage.go)
- internal/setup (setup.go, step_select.go, step_group.go)

tui additions:
- TextInput.CursorEnd() — required by aliasmgr's editInput flow

Verification:
- grep "charmbracelet" --include="*.go" | grep -v internal/tui  →  zero
- go build ./... ✓
- go test -timeout 5m ./... ✓ (all packages green)
- golangci-lint run ✓ (0 issues)

The strict eviction seam is fully sealed: bubbletea/lipgloss/bubbles are
import-visible to internal/tui only. Bubbletea eviction is now a pure
internal/tui rewrite.
…ules

Two new conventions documented in AGENTS.md, derived from this PR's
work:

- No comments in production Go code. Comments that can become code
  should become code. Permitted markers: //go:build, package doc
  (single line), // DECISION:, // TODO:, // FIXME:, // HACK:. Test
  files exempt.

- Direct bubbletea/lipgloss/bubbles imports outside internal/tui are
  a regression. The strict eviction seam is enforced by convention;
  a single grep is the check.

These rules formalize what the cleanup PR already established and
verified end-to-end.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Important

Review skipped

Too many files!

This PR contains 153 files, which is 3 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ca29b79e-586f-43fa-9e87-f57e688c61ec

📥 Commits

Reviewing files that changed from the base of the PR and between 62ad2ee and 235144e.

📒 Files selected for processing (153)
  • AGENTS.md
  • internal/add/add.go
  • internal/add/browse.go
  • internal/add/bulk_test.go
  • internal/add/clipboard.go
  • internal/add/clone.go
  • internal/add/dedup.go
  • internal/add/disk.go
  • internal/add/edit.go
  • internal/add/format.go
  • internal/add/gather.go
  • internal/add/github_source.go
  • internal/add/manual.go
  • internal/add/msg.go
  • internal/add/options.go
  • internal/add/register.go
  • internal/add/sidecar.go
  • internal/add/styles.go
  • internal/add/suggestions.go
  • internal/add/tui.go
  • internal/add/tui_test.go
  • internal/agent/chip_action.go
  • internal/agent/edit_project.go
  • internal/agent/flash.go
  • internal/agent/forms.go
  • internal/agent/header.go
  • internal/agent/items.go
  • internal/agent/lang.go
  • internal/agent/launcher.go
  • internal/agent/list.go
  • internal/agent/persist.go
  • internal/agent/render.go
  • internal/agent/sessions.go
  • internal/agent/source.go
  • internal/agent/stamp.go
  • internal/agent/styles.go
  • internal/agent/tui.go
  • internal/agent/types.go
  • internal/agent/whichkey.go
  • internal/agent/worktrees.go
  • internal/alias/conflict.go
  • internal/alias/generate.go
  • internal/alias/install.go
  • internal/alias/resolve.go
  • internal/alias/shell_zsh.go
  • internal/aliasmgr/model.go
  • internal/aliasmgr/step_confirm.go
  • internal/aliasmgr/step_manage.go
  • internal/auth/auth.go
  • internal/auth/device_flow.go
  • internal/auth/pat.go
  • internal/benchfixture/fixture.go
  • internal/bootstrap/bootstrap.go
  • internal/bootstrap/sidecar.go
  • internal/branchprompt/branchprompt.go
  • internal/branchprompt/branchprompt_test.go
  • internal/branchprompt/messages.go
  • internal/cli/add.go
  • internal/cli/alias.go
  • internal/cli/auth.go
  • internal/cli/bootstrap.go
  • internal/cli/bootstrap_model.go
  • internal/cli/bootstrap_view.go
  • internal/cli/create.go
  • internal/cli/daemon.go
  • internal/cli/doctor.go
  • internal/cli/explorer.go
  • internal/cli/favorite.go
  • internal/cli/migrate.go
  • internal/cli/migrate_model.go
  • internal/cli/migrate_tui.go
  • internal/cli/path.go
  • internal/cli/root.go
  • internal/cli/scan.go
  • internal/cli/setup.go
  • internal/cli/status.go
  • internal/cli/sync.go
  • internal/cli/sync_resolve.go
  • internal/cli/worktree.go
  • internal/cli/worktree_add.go
  • internal/cli/worktree_rm.go
  • internal/clipboard/clipboard.go
  • internal/clipboard/clipboard_test.go
  • internal/clone/clone.go
  • internal/config/branch.go
  • internal/config/config.go
  • internal/config/legacy.go
  • internal/config/machine.go
  • internal/config/project.go
  • internal/config/validate.go
  • internal/conflict/conflict.go
  • internal/conflict/notify.go
  • internal/create/cmd.go
  • internal/create/create.go
  • internal/create/gh.go
  • internal/create/options.go
  • internal/create/render.go
  • internal/create/runner.go
  • internal/create/sidecar.go
  • internal/create/tui.go
  • internal/daemon/config.go
  • internal/daemon/conflicts.go
  • internal/daemon/daemon.go
  • internal/daemon/git.go
  • internal/daemon/ipc_client.go
  • internal/daemon/projects.go
  • internal/daemon/reconciler.go
  • internal/daemon/socket.go
  • internal/daemon/toml.go
  • internal/daemon/watcher.go
  • internal/docs/agent.go
  • internal/docs/schema.go
  • internal/doctor/doctor.go
  • internal/doctor/format.go
  • internal/doctor/project.go
  • internal/doctor/system.go
  • internal/git/bare.go
  • internal/git/git.go
  • internal/git/worktree.go
  • internal/github/app_provider.go
  • internal/github/cache.go
  • internal/github/client.go
  • internal/github/gh_client.go
  • internal/github/github.go
  • internal/github/http_client.go
  • internal/github/provider.go
  • internal/github/resolve.go
  • internal/layout/layout.go
  • internal/migrate/check.go
  • internal/migrate/git_helpers.go
  • internal/migrate/hooks.go
  • internal/migrate/migrate.go
  • internal/migrate/resolve.go
  • internal/migrate/sidecar.go
  • internal/setup/setup.go
  • internal/setup/step_group.go
  • internal/setup/step_select.go
  • internal/sidecar/sidecar.go
  • internal/testutil/gitfixture.go
  • internal/tui/dialog.go
  • internal/tui/dialog_test.go
  • internal/tui/form.go
  • internal/tui/form_test.go
  • internal/tui/keys.go
  • internal/tui/list.go
  • internal/tui/list_test.go
  • internal/tui/palette.go
  • internal/tui/runtime.go
  • internal/tui/state.go
  • internal/tui/state_test.go
  • internal/tui/style.go
  • internal/tui/tui.go
  • internal/tui/widgets.go

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/cleanup-and-tui-unification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c7ffbaa71

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/tui/keys.go
KeyTab: "tab",
KeyShiftTab: "shift+tab",
KeyBackspace: "backspace",
KeySpace: "space",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Bubble Tea's space key string

When a real TUI user presses Space, keyMsgFromBubbletea maps Bubble Tea's KeySpace to this KeySpace, and String() now returns "space". Existing handlers still switch on " " for selection/toggle flows such as setup repo selection and add bulk selection, so those documented Space actions no longer fire unless tests synthesize Space as KeyRunes. Please keep the wrapper's String() compatible with the prior tea.KeyMsg.String() behavior or update all consumers consistently.

Useful? React with 👍 / 👎.

Comment thread internal/tui/runtime.go
Comment on lines +130 to +131
tea.KeyCtrlC: KeyCtrlC,
tea.KeyCtrlD: KeyCtrlD,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve non-C/D control shortcuts

The new key translation only recognizes Ctrl+C and Ctrl+D, so Bubble Tea key types for Ctrl+S/Ctrl+Q/Ctrl+A/Ctrl+N/Ctrl+P fall through as an empty KeyRunes value and KeyMsg.String() never matches the existing shortcuts. This breaks real TUI shortcuts I checked in ws agent (ctrl+s shell launch and ctrl+q quit), setup (ctrl+a/ctrl+s), and alias management (ctrl+n/ctrl+p). Please map the remaining control key types or derive the wrapper string from Bubble Tea's key string.

Useful? React with 👍 / 👎.

CI Format check failed because two files were committed with non-gofmt
formatting:
- state.go: long one-liner Done() func body
- widgets.go: alignment column off in Spinner.View/Tick and
  ownToTeaKeyType[KeyRunes]

Pure formatting; no behavior change. Build, race tests, lint all green.
@kuchmenko kuchmenko merged commit 31627ef into main May 19, 2026
11 checks passed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 235144e536

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/tui/keys.go
Comment on lines +30 to +35
type KeyMsg struct {
Type KeyType
Runes []rune
Alt bool
Ctrl bool
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve paste metadata in key messages

When Bubble Tea sends bracketed paste input, its key message carries paste metadata so pasted text does not compare equal to normal shortcut keys. The wrapper drops that flag here, so in real TUI sessions pasting a single shortcut character in non-input views (for example q in the agent list or d on a worktree row) is indistinguishable from pressing that key and can quit or start deletion. Please carry the paste flag through the wrapper and keep String() from matching shortcuts for pasted text.

Useful? React with 👍 / 👎.

Comment thread internal/tui/tui.go
Comment on lines +1 to +5
// Package tui owns every interactive primitive used by the workspace
// CLI. It is the only place in the codebase that imports a TUI
// framework. The Msg / Cmd / Model / Style types are nominally
// distinct from bubbletea's, so a future eviction is a pure
// internal/tui rewrite with no consumer changes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep production package docs to one line

/workspace/workspace/AGENTS.md says production Go should have no comments, with package doc comments permitted only when they are one line and required by go doc. This newly added production package comment is a five-line narrative block, so it violates the repository rule introduced in the same change; please remove it or reduce it to the required one-line package doc.

Useful? React with 👍 / 👎.

kuchmenko added a commit that referenced this pull request May 19, 2026
Criterion: a file earns its size if it reads top-to-bottom as one
narrative. Not by LOC. Big is fine when there's a through-line; small
is wrong when the next thing you need is in another file.

By that yardstick most of the 150 files were small *and* wrong — split
per-view-mode, per-syscall, per-subcommand. Mechanical consolidation,
no behavior change.

Results:
  150 -> 45 production .go files (-70%)
  25  -> 21 folders under internal/
  20,468 -> 19,592 LOC (dedup'd imports)
  binary size unchanged (14 MB)
  zero charmbracelet imports outside internal/tui/ (PR #49 seam intact)

Major package collapses:
  cli     28 -> 5  (root + bootstrap + migrate + sync + worktree + docs)
  agent   19 -> 3  (model -> view -> tui)
  add     18 -> 4  (add -> sources -> suggest -> tui)
  daemon  10 -> 3  (daemon -> reconciler -> ipc)
  create   8 -> 2  (create -> tui)

Ten small packages each collapse to one file at internal/<pkg>/<pkg>.go:
alias, aliasmgr, auth, bootstrap, config, doctor, git, github, migrate,
setup.

Four folders deleted: clipboard folds into add, clone folds into git,
benchfixture moves to bench/, docs folds into cli.

Two intentional outliers: cli/root.go (1,727 LOC) and agent/tui.go
(1,774 LOC) both walk top-to-bottom — root.go is 13 declarative Cobra
builders in a row; agent/tui.go is state -> update -> panels. Splitting
either would cut a sentence in half.

Queued, not in this PR: bubbletea eviction (drops internal/tui/ 10 -> ~3)
and a few cli-only folds (aliasmgr, setup, doctor, create, bootstrap,
migrate could fold into cli). Both deferred so this PR stays a pure
mechanical shrink.
kuchmenko added a commit that referenced this pull request May 19, 2026
Criterion: a file earns its size if it reads top-to-bottom as one
narrative. Not by LOC. Big is fine when there's a through-line; small
is wrong when the next thing you need is in another file.

By that yardstick most of the 150 files were small *and* wrong — split
per-view-mode, per-syscall, per-subcommand. Mechanical consolidation,
no behavior change.

Results:
  150 -> 45 production .go files (-70%)
  25  -> 21 folders under internal/
  20,468 -> 19,592 LOC (dedup'd imports)
  binary size unchanged (14 MB)
  zero charmbracelet imports outside internal/tui/ (PR #49 seam intact)

Major package collapses:
  cli     28 -> 5  (root + bootstrap + migrate + sync + worktree + docs)
  agent   19 -> 3  (model -> view -> tui)
  add     18 -> 4  (add -> sources -> suggest -> tui)
  daemon  10 -> 3  (daemon -> reconciler -> ipc)
  create   8 -> 2  (create -> tui)

Ten small packages each collapse to one file at internal/<pkg>/<pkg>.go:
alias, aliasmgr, auth, bootstrap, config, doctor, git, github, migrate,
setup.

Four folders deleted: clipboard folds into add, clone folds into git,
benchfixture moves to bench/, docs folds into cli.

Two intentional outliers: cli/root.go (1,727 LOC) and agent/tui.go
(1,774 LOC) both walk top-to-bottom — root.go is 13 declarative Cobra
builders in a row; agent/tui.go is state -> update -> panels. Splitting
either would cut a sentence in half.

Queued, not in this PR: bubbletea eviction (drops internal/tui/ 10 -> ~3)
and a few cli-only folds (aliasmgr, setup, doctor, create, bootstrap,
migrate could fold into cli). Both deferred so this PR stays a pure
mechanical shrink.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant