Skip to content

showagent 0.7.0 — every agent, every OS#1

Merged
aytzey merged 20 commits into
mainfrom
release/v0.7.0
Jul 8, 2026
Merged

showagent 0.7.0 — every agent, every OS#1
aytzey merged 20 commits into
mainfrom
release/v0.7.0

Conversation

@aytzey

@aytzey aytzey commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Release branch for v0.7.0. Everything below is on this branch and gated by the hardened CI (vet, golangci-lint, race tests, cross-compiles for all five release targets).

New agents

  • feat(session): add OpenCode provider backed by the opencode CLI (653a985)
  • feat(session): add Gemini CLI provider (a21aad0)
  • refactor(session): introduce provider registry replacing per-provider dispatch (0ceacd1) — a provider is now one self-contained file implementing a small interface; badges, filter keys, and convert targets derive from the registry

Multi-OS

  • feat: support Windows launch, multi-OS release archives, and installer (cdcccec) — darwin amd64/arm64, windows amd64 added to releases; Windows resume uses a run-and-wait fallback instead of unix exec; scripts/install.sh added

Real CLI surface

  • feat(cli): add help, version, list, and resume commands (2f01454) — --help/--version, list --json, resume <id|latest>
  • test(cli): cover arg parsing, list --json shape, and resume resolution (437789c)

Safer conversions and scanning

  • fix(session): atomic converted-session writes, scan hardening, linear reverseLines (783dae4) — temp-file + rename so a crash never corrupts another CLI's session store
  • feat(session): add scan-target reporting and pre-launch validation (ac20863)

TUI overhaul

  • fix(tui): return longest fitting suffix in rightCells (81a7668) — path truncation no longer amputates the project basename
  • feat(tui): rework keybindings, validate before exec, and fix small-terminal layout (8db4e2c) — BREAKING: provider filters move to number keys, 'd' now deletes; 'r' rescans; launch errors surface in-TUI; help bar survives 80x24
  • docs(readme): update keybinding table for the reworked picker keys (39d1997)

Quality and CI

  • fix(lint): make the tree golangci-lint clean and add the lint config (48e29c9)
  • ci: gate on vet, golangci-lint, race tests, and cross-compiles (eca5908)
  • chore(deps): bump patch-level dependencies and go directive to 1.25.9 (ccdffd5)

Docs and demo

  • feat(demo): add hermetic GIF recording scaffold (5fb3670)
  • feat(demo): record the hero GIF with Gemini fixtures and final keys (ba3c36e)
  • docs(readme): rewrite around the browse+convert wedge for v0.7.0 (f9ba9ee)

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Summary by CodeRabbit

  • New Features

    • Added support for more session providers, including Gemini and OpenCode.
    • Introduced new CLI actions for listing sessions in JSON, resuming by ID or latest, and setting up compound-engineering support.
    • Expanded release builds to include macOS, Windows, and Linux downloads with checksums.
  • Bug Fixes

    • Improved handling of large or malformed session files so discovery is more reliable.
    • Made session writes safer to reduce the chance of partial files after failures.
  • Documentation

    • Refresh a large portion of the README and demo instructions, including install and usage guidance.

aytzey and others added 20 commits July 8, 2026 23:15
rightCells iterated from i=len(runes) downward, so the empty suffix
always matched first and it returned "" for any value wider than the
target. truncateMiddle therefore degenerated to head-truncation and
long workspace paths lost their basename in list rows, group headers,
the detail panel, and piped table output. Iterate from the smallest
index upward so the first fitting candidate is the longest suffix,
keeping the existing width accounting intact.

Adds regression tests: basename stays visible for long paths, exact
fit, widths smaller than the ellipsis, and wide (CJK) runes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The !unix build previously stubbed session launch with an error, which
made resume, hand-off, and compound unusable on Windows. Replace the
stub with a child-process fallback that attaches the terminal and exits
with the child's exit code, mirroring the Unix exec-replacement path as
closely as the platform allows. The now-unused errNoExec helper is
removed.

Extend the release workflow from linux-only binaries to a matrix of
linux/darwin x amd64/arm64 plus windows/amd64, packaged as tar.gz (zip
on Windows) with LICENSE and README, raw binaries kept alongside, and a
single SHA256SUMS covering everything. Builds stamp main.version via
-ldflags so tagged releases report their version once the version var
lands.

Add scripts/install.sh, a POSIX installer that detects OS/arch, fetches
the right archive from the latest release, verifies it against
SHA256SUMS, and installs to ~/.local/bin with a /usr/local/bin sudo
fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… reverseLines

Converted sessions are written into another tool's session store, so a
crash mid-write used to leave a half-encoded file that Codex, Claude
Code, or jcode could then try to load. All three converted-session
writers (and the branch path, which shares them) now stream into a temp
file in the destination directory, fsync, and rename into place.

Discovery scanners previously ignored scanner.Err(), so a file with an
unscannable line (for example one beyond scanBufferMax) was listed
half-parsed. The codex and claude forward scans now skip such files
deliberately, matching the conversion paths, and the codex reverse-scan
preview documents its degrade-to-empty behavior.

reverseLines prepended each block onto the carried buffer, which is
O(n^2) and unbounded on newline-poor files. It now collects bounded
line fragments per block and caps buffering at scanBufferMax, degrading
an oversized line to its trailing bytes instead of loading a whole
multi-gigabyte file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds demo/ with everything needed to record the README demo without
touching real session stores or launching real agents:

- demo/fixtures/gen.sh fabricates a fake HOME with 5 Codex and 5 Claude
  Code sessions across three fake workspaces, in the exact JSONL shapes
  the discovery parsers read, with timestamps relative to record time so
  the TUI shows fresh "2h ago" rows. All content is invented.
- demo/bin/codex and demo/bin/claude are stub CLIs that print a mock
  "session resumed" screen, so the convert-then-resume beat works.
- demo/demo.tape is the vhs storyboard: browse, search, preview toggle,
  convert to Claude with x, resume, end card. Not recorded yet on
  purpose: keybindings may still change before release.
- demo/README.md documents the regeneration recipe; generated .home and
  .build directories are gitignored.

Verified end to end: gen.sh output parses through session.Discover
(all 10 rows appear with first and last user previews) and the tape
passes vhs validate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
showagent previously understood only 'setup' and rejected everything
else, which made it hard to script and hard to discover. The CLI now
has a real surface:

- --help/-h prints usage, subcommands, key picker bindings, and the
  session directories with their env overrides.
- --version/-v prints the ldflags-stamped version, falling back to Go
  module build info so 'go install ...@latest' builds report a version.
- 'list' prints the plain table without the TUI; the table now includes
  the full session id so output feeds 'showagent resume'. '--json'
  emits a stable machine-consumable array of
  {id, provider, workspace, updated, first_message, last_message}.
- 'resume <id|latest> [--yolo]' resumes one session directly; unknown
  ids exit 1 and point at 'showagent list'.
- Unknown arguments exit 2 with a short usage line.

PrintTable takes the detected terminal width instead of hardcoding 120
(still the fallback when the width is undetectable, e.g. piped output).
github.com/charmbracelet/x/term was already an indirect dependency of
bubbletea; it is only promoted to direct for the size lookup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exercise the new CLI surface end to end against hermetic fixture homes
(CODEX_HOME/CLAUDE_HOME/JCODE_HOME overrides): help and version exit 0
with the documented content, unknown arguments exit 2 with a usage
hint, 'list' prints full session ids, 'list --json' emits the stable
field names with RFC3339 timestamps (and a valid empty array with no
sessions), and resume resolves 'latest'/exact ids while unknown ids
exit 1 pointing at 'showagent list'. PrintTable gets direct coverage
for the id column and the 120-column width fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ScanTargets exposes the exact directories Discover reads plus their env
overrides, so the TUI empty state and the CLI can tell the user where
sessions are expected instead of a bare 'none found'. ValidateResume and
ValidateCompound check the provider CLI and workspace directory up front,
letting the picker surface launch failures in a status line before the
TUI is torn down for exec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rminal layout

Keybindings: provider visibility moves from c/d/z to index-based digits
(1..9 over the discovered provider order, so new providers scale without
new letters), freeing 'd' as a delete alias with the same two-press
confirmation. f/l/b collapse into a single 'p' preview cycle
(first -> latest -> both), 'r' rescans the session stores while keeping
the cursor, search, and provider filters, and esc only peels back UI
state (search, help overlay, armed delete) - it never quits; only q and
ctrl+c do. The short help bar and the '?' overlay now show live values:
per-provider on/off state, current transfer target and scope, preview
mode, and resume mode.

Pre-exec validation: enter and the compound chooser validate the
provider CLI and workspace before tea.Quit tears the TUI down, so a
missing binary or deleted workspace becomes a status line instead of a
post-exit error; hand-off targets require the target CLI on PATH.

Layout: the browse view enforces a hard height budget so the help bar
always keeps its rows, the header drops stat chips instead of wrapping,
and the detail panel truncates lines to the real text width (Style.Width
covers border and padding, so the old inner width wrapped long paths and
pushed the help bar off an 80x24 screen). /home/aytzey collapses to ~ in row,
detail, and empty-state paths. The empty state and the piped-mode error
now list the exact scanned directories with their env overrides and say
how to get a first session listed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Digits toggle providers, 'p' cycles the preview, 'r' rescans, 'd' joins
delete/backspace, and esc no longer quits. A later docs pass will polish
wording; this keeps the table from contradicting the binary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… dispatch

A provider is now one file implementing the ProviderImpl interface
(Name/DisplayName/CommandName/Home/ScanTarget/Discover/ResumeArgs/
CompoundArgs/Delete/Transcript/WriteConverted) plus one entry in the
registry slice. This collapses the ~12 hardcoded switch sites across
session.go, handoff.go, compound.go, table.go, theme.go and tui.go so
new agents (OpenCode, Gemini) land as self-contained files instead of
cross-cutting edits.

Behavior is unchanged for codex/claude/jcode: resume/compound argv,
delete semantics, scan targets, and the jcode binary gating all moved
verbatim into the provider impls, and the existing test suite passes
untouched. The TUI compound chooser and provider badges now derive
from the registry (digit order = registry order), with a neutral chip
fallback for providers without a dedicated accent. launch() now
rejects an empty argv instead of panicking if a row carries an
unknown provider.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OpenCode stores sessions in SQLite (<XDG data>/opencode/opencode*.db),
not JSON files: the storage/session/<project>/<id>.json tree is a
legacy layout that releases since early 2026 migrate into the database
and never write again. Verified against anomalyco/opencode at tag
v1.17.15: packages/core/src/global.ts (xdg-basedir data dir),
packages/core/src/database/database.ts (db path and channel suffix),
packages/core/src/session/sql.ts (session/message/part tables),
packages/schema/src/identifier.ts (ses_/msg_/prt_ id scheme).

Rather than parsing SQLite (a new dependency) or shipping a parser for
the dead JSON layout, every operation shells out to the user's own
opencode CLI, so schema drift stays opencode's problem:

  discover   opencode db --format json "<SQL>"  (cli/cmd/db.ts)
  transcript opencode export <id>                (cli/cmd/export.ts)
  delete     opencode session delete <id>        (cli/cmd/session.ts)
  convert-to opencode import <file>              (cli/cmd/import.ts)
  resume     opencode --session <id> [--auto] [--prompt] (cli/cmd/tui.ts)

The provider engages only when the opencode binary is on PATH and a
database exists, so showagent never causes opencode to create data as
a side effect; OPENCODE_DATA_HOME overrides the data dir so the test
suite stays hermetic (all Discover-calling tests now pin it). Both
convert directions are supported: transcripts come from export, and
conversions into opencode synthesize an export-format payload that
opencode import validates and writes itself. Tests cover discovery,
gating on binary/database, delete, resume/compound argv, id shape,
and one conversion each way against a fake opencode CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gemini CLI records chats under ~/.gemini/tmp/<project>/chats/
session-<timestamp>-<sid8>.json[l], project-scoped by workspace
directory. Verified against google-gemini/gemini-cli at commit
172ff92c (main, 2026-07-08) and a live 0.18.4 install:

  packages/core/src/utils/paths.ts             getProjectHash (sha256
                                               of the workspace path),
                                               GEMINI_CLI_HOME override
  packages/core/src/config/storage.ts          getProjectTempDir, legacy
                                               hash-dir migration
  packages/core/src/config/projectRegistry.ts  projects.json path->slug
                                               registry, .project_root
                                               ownership markers
  packages/core/src/services/chatRecordingService.ts
                                               file naming, JSONL record
                                               replay ($set, $rewindTo,
                                               in-place id updates),
                                               legacy whole-file fallback
  packages/core/src/services/chatRecordingTypes.ts
                                               ConversationRecord schema
  packages/cli/src/utils/sessionUtils.ts       --resume uuid|index|latest

Discovery walks the tmp dirs and replays both formats, resolving each
project dir back to its workspace via the .project_root marker, then
the projects.json registry; legacy sha256 dirs stay "(unknown cwd)"
because the hash is one-way. Subagent sessions and sessions without
resumable content are skipped, matching what `gemini --resume` offers.
Resume is `gemini [--yolo] --resume <sessionId>` run inside the
workspace (the store is project-scoped); compound adds
--prompt-interactive, which does not conflict with --resume.

Conversions into gemini write a single-line legacy-format document:
current releases parse one-line legacy records in their line reader
(and pretty documents via the whole-file fallback) and migrate them to
JSONL on first resume, while old releases read .json natively. The
file lands in the workspace's existing marker/registry project dir,
falling back to the sha256 name that current releases migrate on
startup. Verified end-to-end: a hermetic gemini 0.18.4
(HOME+GEMINI_CLI_HOME pinned) lists the synthesized session with the
right title and id.

GEMINI_CLI_HOME is the CLI's own home override, so the test suite
stays hermetic by pinning it (all Discover-calling tests now do).
Tests cover discovery of both formats, cwd resolution via marker and
registry, subagent/contentless skipping, rewind/checkpoint replay,
resume/compound argv, delete, both convert directions, and a
codex->gemini->transcript round trip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix every finding from golangci-lint's standard set plus unparam so CI
can gate on a zero-issue run:

- errcheck: mark the terminal writes in cmd/showagent/main.go with an
  explicit "_, _ =" (writes to stdout/stderr have no meaningful error
  handling in a CLI), and ignore the error of deferred Close calls on
  read-only files in claude.go, codex.go, gemini.go, handoff.go,
  session.go, and main_test.go.
- staticcheck QF1001: rewrite the negated comparisons in the opencode id
  ordering test.
- unparam: drop the always-1 seq parameter from opencodeID, the
  constant gutter result from tableWidths, and the always-firstMessage
  mode parameter from newModel.

.golangci.yml pins the standard set plus unparam and deliberately skips
style-only linters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The old workflow only ran go test and go build. Now every push to main
and every PR runs go vet, golangci-lint (pinned to the same v2.12.2
used locally), the full test suite under -race, and a host build, plus
a CGO_ENABLED=0 cross-compile matrix for linux/amd64, darwin/amd64,
darwin/arm64, and windows/amd64 so a platform-specific break (build
tags, syscalls) is caught before the release workflow hits it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pick up the current patch releases before cutting v0.7.0: bubbles
v2.1.1, bubbletea v2.0.8, lipgloss v2.0.5, x/sys v0.47.0, and fuzzy
v0.1.3, along with the transitive ultraviolet, go-runewidth, and
x/sync updates go mod tidy pulls in. Full suite passes under -race
and golangci-lint stays at zero issues on the new versions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tape still drove the retired l/b preview keys; the picker now cycles
previews on p, so the recording is redone against the shipped bindings.
gen.sh grows two fabricated Gemini CLI sessions so the hero GIF shows
three provider badges, and the tape pins GEMINI_CLI_HOME and
OPENCODE_DATA_HOME inside demo/.home so a recorder's real stores can
never leak into the frame. Output moves to docs/demo.gif (21.6s, 1.6MB)
where the README embeds it, plus docs/social-preview.png (1280x640)
lifted from the convert moment for the GitHub social card.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The old README undersold what landed in v0.7.0: it named two agents,
listed no CLI surface, and led with implementation details instead of
the differentiator. The rewrite leads with the hero GIF and the honest
wedge (the only TUI combining browse, search, resume, branch, and
convert across agents), documents all five providers with accurate
convert directions and env overrides, adds the install matrix (brew tap
marked available after v0.7.0), the verified keybinding table, the
list --json contract, a factual comparison with claude-squad, ccmanager,
and Agent Sessions, an FAQ covering locality and what conversion
intentionally does not copy, and a pointer for adding a provider in
about 250 lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a provider registry abstraction replacing switch-based dispatch for session providers, introduces Gemini and OpenCode providers, refactors the CLI into testable subcommands (list/resume/setup), reworks TUI key bindings/rendering with provider-based badges and rescan/validation flows, hardens file scanning/writes, and updates CI, release, install, and demo tooling.

Estimated code review effort: 5 (Critical) | ~150 minutes

Changes

showagent core changes

Layer / File(s) Summary
Provider registry abstraction
internal/session/provider.go, internal/session/compound.go, internal/session/handoff.go, internal/session/session.go
Introduces ProviderImpl interface, ordered registry, providerFor/ProviderOrder/DisplayName/CompoundAgents, and refactors compound command building, conversion, transcript extraction, discovery, and deletion to dispatch through the registry instead of switch statements; adds ScanTargets, ValidateResume/ValidateCompound, and rewrites reverseLines.
Existing provider hardening
internal/session/codex.go, internal/session/claude.go, internal/session/jcode.go, internal/session/exec_other.go
Adds scanner-error handling to drop malformed files from discovery, adopts atomic file writes, adds explicit errors for missing session files, and implements a real non-Unix syscallExec.
Gemini provider
internal/session/gemini.go, internal/session/gemini_test.go
Adds a Gemini session provider supporting discovery, JSONL replay with rewind/checkpoint handling, legacy format parsing, transcript extraction, and conversion, with extensive tests.
OpenCode provider
internal/session/opencode.go, internal/session/opencode_test.go
Adds an OpenCode session provider backed by the opencode CLI for discovery, export-based transcripts, import-based conversion, and ID generation, with tests.
Discovery hardening and env wiring
internal/session/hardening_test.go, internal/session/session_test.go
Adds tests for atomic-write failure safety and reverseLines edge cases, and updates existing tests to configure new provider environment variables.
CLI dispatcher refactor
cmd/showagent/main.go, cmd/showagent/main_test.go
Refactors CLI into a testable run() dispatcher with list, resume, setup subcommands, exit-code-based error handling, and comprehensive tests.
TUI table rendering
internal/tui/table.go, internal/tui/table_test.go, internal/tui/theme.go
Makes PrintTable width-driven, refactors column layout, and centralizes provider badge styling via a theme map.
TUI interaction and validation
internal/tui/keys.go, internal/tui/tui.go, internal/tui/tui_test.go
Adds digit-based provider filtering and rescan bindings, reworks model state/rendering for rescan/compound/resume validation and empty-state scan targets, with extensive tests.
CI, lint, and release workflows
.github/workflows/ci.yml, .github/workflows/release.yml, .golangci.yml, go.mod
Adds permissions, go vet/lint/race steps, a golangci-lint config, and multi-OS/arch release packaging with aggregated checksums; bumps Go and dependency versions.
Install script and demo assets
scripts/install.sh, demo/*
Adds a standalone install script and demo recording tooling (fixture generator, stub CLIs, VHS tape, README, gitignore).
README rewrite
README.md
Rewrites installation, quick start, keybindings, scripting contract, comparison, compound engineering, FAQ, and provider-extension documentation.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main release theme: v0.7.0 with broader agent and OS support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v0.7.0

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.

@aytzey aytzey merged commit 962f5c0 into main Jul 8, 2026
5 of 6 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)

15-15: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Set persist-credentials: false on checkout steps.

Both actions/checkout steps persist the GitHub token in .git/config by default. Subsequent steps executing untrusted code (e.g., go test running dependency code, go build executing build scripts from modules) could exfiltrate it. Neither job needs git push access.

🔒 Proposed fix for both checkout steps
       - uses: actions/checkout@v7.0.0
+        with:
+          persist-credentials: false
       - uses: actions/setup-go@v6.4.0

Apply the same change to the checkout in the cross-compile job (line 41).

Also applies to: 41-41

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 15, The checkout steps in the CI workflow
are leaving the GitHub token persisted in git config, which can be exposed to
later untrusted build/test commands. Update both uses of actions/checkout in the
workflow to set persist-credentials to false, and keep the change consistent
across the checkout step in the main job and the one in cross-compile so neither
job retains push credentials.

Source: Linters/SAST tools

🧹 Nitpick comments (2)
internal/session/opencode.go (1)

450-458: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

decodeOpenCodeJSON can latch onto a bracket inside preamble text.

bytes.IndexAny(output, "[{") returns the first [ or { anywhere in the output, so a banner/notice line containing either character before the real payload would make json.Unmarshal start mid-noise and fail — contrary to the "tolerate banner lines" intent. Consider scanning line-by-line for the first line that begins (after trimming) with [ or {, or matching the trailing JSON.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/session/opencode.go` around lines 450 - 458, decodeOpenCodeJSON is
too permissive because it uses bytes.IndexAny and can start parsing from a stray
[ or { in banner text instead of the real JSON payload. Update
decodeOpenCodeJSON to locate the first actual JSON line in the output, such as
by scanning line-by-line in the decodeOpenCodeJSON helper and selecting the
first trimmed line that begins with [ or {, or by otherwise matching the real
trailing JSON block before calling json.Unmarshal. Keep the existing tolerance
for preamble text, but avoid latched parsing from arbitrary brackets inside
notices.
internal/tui/tui.go (1)

342-358: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider renaming the local key variable to avoid shadowing the key package import.

key := msg.String() at line 345 shadows the imported key package within this block. While the block returns before any key.Matches call, a future edit adding key.Matches here would fail to compile with a confusing error.

♻️ Optional rename
  if m.compoundChoosing {
-   key := msg.String()
+   keyStr := msg.String()
    if agents := session.CompoundAgents(); len(keyStr) == 1 && keyStr[0] >= '1' && keyStr[0] <= '9' {
      if index := int(keyStr[0] - '1'); index < len(agents) {
        return m.startCompound(agents[index])
      }
    }
-   switch key {
+   switch keyStr {
    case "esc", "q", "ctrl+c":
      m.compoundChoosing = false
      m.compoundRow = nil
      m.compoundNotice = ""
      return m, nil
    }
    return m, nil
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/tui.go` around lines 342 - 358, Rename the local variable in the
compound chooser block inside the TUI handling logic to avoid shadowing the
imported key package; the `key := msg.String()` declaration in the
`m.compoundChoosing` branch conflicts with later use of `key.Matches` elsewhere
in `internal/tui/tui.go`. Update the variable name in that block and all
references to it in the same scope so the code stays clear and future
key-matching edits won’t run into a package-name collision.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Line 28: Both checkout steps currently persist the GitHub token in git config
by default, so update the `actions/checkout` usage in both the `release` job and
the other checkout step to set `persist-credentials: false`. Keep the change
localized to the `checkout` action invocations in the workflow so `gh release
create` continues to use `GH_TOKEN` instead of stored git credentials.

In `@cmd/showagent/main.go`:
- Line 155: The empty-state onboarding hint in main should be updated to reflect
all supported providers, not just codex and claude. Adjust the stderr message
near the showagent startup flow so it stays aligned with the providers exposed
by ScanTargets() and --help, including gemini, opencode, and jcode, and keep the
wording generic enough that any supported agent can be used to start a
conversation.

In `@go.mod`:
- Around line 3-28: Update the go.mod dependency on golang.org/x/sys because the
current v0.47.0 version is not a published release and prevents module
resolution. Replace it with a valid released version that is compatible with the
existing charm.land/bubbles/v2, charm.land/bubbletea/v2, and
charm.land/lipgloss/v2 dependencies, then verify the module graph still resolves
cleanly.

In `@internal/session/codex.go`:
- Around line 46-57: The codexProvider.Delete shell-out can block forever
because command.CombinedOutput has no timeout; update Delete to use
exec.CommandContext with a timeout-aware context so a hung codex CLI can be
canceled. Create the command from the context before setting command.Dir, keep
the existing cwd behavior and error wrapping, and make sure the timeout is
applied specifically around the codex delete invocation in codexProvider.Delete.

---

Outside diff comments:
In @.github/workflows/ci.yml:
- Line 15: The checkout steps in the CI workflow are leaving the GitHub token
persisted in git config, which can be exposed to later untrusted build/test
commands. Update both uses of actions/checkout in the workflow to set
persist-credentials to false, and keep the change consistent across the checkout
step in the main job and the one in cross-compile so neither job retains push
credentials.

---

Nitpick comments:
In `@internal/session/opencode.go`:
- Around line 450-458: decodeOpenCodeJSON is too permissive because it uses
bytes.IndexAny and can start parsing from a stray [ or { in banner text instead
of the real JSON payload. Update decodeOpenCodeJSON to locate the first actual
JSON line in the output, such as by scanning line-by-line in the
decodeOpenCodeJSON helper and selecting the first trimmed line that begins with
[ or {, or by otherwise matching the real trailing JSON block before calling
json.Unmarshal. Keep the existing tolerance for preamble text, but avoid latched
parsing from arbitrary brackets inside notices.

In `@internal/tui/tui.go`:
- Around line 342-358: Rename the local variable in the compound chooser block
inside the TUI handling logic to avoid shadowing the imported key package; the
`key := msg.String()` declaration in the `m.compoundChoosing` branch conflicts
with later use of `key.Matches` elsewhere in `internal/tui/tui.go`. Update the
variable name in that block and all references to it in the same scope so the
code stays clear and future key-matching edits won’t run into a package-name
collision.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff4d9d5a-964b-4b88-ba71-d588e7c7eeda

📥 Commits

Reviewing files that changed from the base of the PR and between 03f7c58 and f9ba9ee.

⛔ Files ignored due to path filters (3)
  • docs/demo.gif is excluded by !**/*.gif
  • docs/social-preview.png is excluded by !**/*.png
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (34)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .golangci.yml
  • README.md
  • cmd/showagent/main.go
  • cmd/showagent/main_test.go
  • demo/.gitignore
  • demo/README.md
  • demo/bin/claude
  • demo/bin/codex
  • demo/demo.tape
  • demo/fixtures/gen.sh
  • go.mod
  • internal/session/claude.go
  • internal/session/codex.go
  • internal/session/compound.go
  • internal/session/exec_other.go
  • internal/session/gemini.go
  • internal/session/gemini_test.go
  • internal/session/handoff.go
  • internal/session/hardening_test.go
  • internal/session/jcode.go
  • internal/session/opencode.go
  • internal/session/opencode_test.go
  • internal/session/provider.go
  • internal/session/session.go
  • internal/session/session_test.go
  • internal/tui/keys.go
  • internal/tui/table.go
  • internal/tui/table_test.go
  • internal/tui/theme.go
  • internal/tui/tui.go
  • internal/tui/tui_test.go
  • scripts/install.sh

- goos: windows
goarch: amd64
steps:
- uses: actions/checkout@v7.0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Set persist-credentials: false on checkout steps.

Both actions/checkout steps persist the GitHub token in .git/config by default. The release job uses GH_TOKEN env var for gh release create, not git credentials, so persist-credentials: false is safe for both jobs.

🔒 Proposed fix for both checkout steps
       - uses: actions/checkout@v7.0.0
+        with:
+          persist-credentials: false
       - uses: actions/setup-go@v6.4.0

Apply the same change to the checkout in the release job (line 65).

Also applies to: 65-65

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 28-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 28, Both checkout steps currently
persist the GitHub token in git config by default, so update the
`actions/checkout` usage in both the `release` job and the other checkout step
to set `persist-credentials: false`. Keep the change localized to the `checkout`
action invocations in the workflow so `gh release create` continues to use
`GH_TOKEN` instead of stored git credentials.

Source: Linters/SAST tools

Comment thread cmd/showagent/main.go
}
_, _ = fmt.Fprintln(stderr, line)
}
_, _ = fmt.Fprintln(stderr, "start a conversation with codex or claude, then run showagent again")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale onboarding hint lists only two of five providers.

The empty-state guidance says to start a session "with codex or claude", but --help and ScanTargets() now cover gemini, opencode, and jcode too. Widen the hint so users with only those agents aren't misled.

✏️ Suggested wording
-	_, _ = fmt.Fprintln(stderr, "start a conversation with codex or claude, then run showagent again")
+	_, _ = fmt.Fprintln(stderr, "start a conversation with a supported agent (codex, claude, gemini, opencode, jcode), then run showagent again")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_, _ = fmt.Fprintln(stderr, "start a conversation with codex or claude, then run showagent again")
_, _ = fmt.Fprintln(stderr, "start a conversation with a supported agent (codex, claude, gemini, opencode, jcode), then run showagent again")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/showagent/main.go` at line 155, The empty-state onboarding hint in main
should be updated to reflect all supported providers, not just codex and claude.
Adjust the stderr message near the showagent startup flow so it stays aligned
with the providers exposed by ScanTargets() and --help, including gemini,
opencode, and jcode, and keep the wording generic enough that any supported
agent can be used to start a conversation.

Comment thread go.mod
Comment on lines +3 to +28
go 1.25.9

require (
charm.land/bubbles/v2 v2.1.0
charm.land/bubbletea/v2 v2.0.7
charm.land/lipgloss/v2 v2.0.4
golang.org/x/sys v0.45.0
charm.land/bubbles/v2 v2.1.1
charm.land/bubbletea/v2 v2.0.8
charm.land/lipgloss/v2 v2.0.5
github.com/charmbracelet/x/term v0.2.2
golang.org/x/sys v0.47.0
)

require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 // indirect
github.com/charmbracelet/x/ansi v0.11.7 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/mattn/go-runewidth v0.0.24 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/sahilm/fuzzy v0.1.3 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sync v0.21.0 // indirect

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Go module versions via the Go module proxy
echo "=== charm.land/bubbles/v2 ==="
curl -s "https://proxy.golang.org/charm.land/bubbles/v2/@v/list"
echo "=== charm.land/bubbletea/v2 ==="
curl -s "https://proxy.golang.org/charm.land/bubbletea/v2/@v/list"
echo "=== charm.land/lipgloss/v2 ==="
curl -s "https://proxy.golang.org/charm.land/lipgloss/v2/@v/list"
echo "=== golang.org/x/sys ==="
curl -s "https://proxy.golang.org/golang.org/x/sys/@v/list"

Repository: aytzey/showagent

Length of output: 1984


🌐 Web query:

proxy.golang.org charm.land/bubbles/v2 v2.1.1 charm.land/bubbletea/v2 v2.0.8 charm.land/lipgloss/v2 v2.0.5 golang.org/x/sys v0.47.0

💡 Result:

As of July 8, 2026, the status of the requested Go modules is as follows: - charm.land/bubbles/v2: The latest version is v2.1.1, released on July 4, 2026 [1]. This is a patch release that fixes a textarea styling bug [2][3]. - charm.land/bubbletea/v2: The latest version is v2.0.8 [4][5]. It includes improvements for specific emoji-rendering edge cases [2][6]. - charm.land/lipgloss/v2: The latest version is v2.0.5, released in late June 2026 [7][8]. It also addresses emoji-rendering issues [9]. - golang.org/x/sys: There is no version v0.47.0 for this module [10][11][12]. Versions for golang.org/x/sys generally follow a different release cadence than packages like golang.org/x/net or golang.org/x/crypto, which did reach v0.47.0 in June 2026 [10][12]. As of early June 2026, golang.org/x/sys was at v0.40.0 [11][12].

Citations:


🌐 Web query:

site:pkg.go.dev "charm.land/bubbles/v2" "v2.1.1"

💡 Result:

charm.land/bubbles/v2 v2.1.1 is a specific version of the Bubbles library, which is a collection of common UI components for Bubble Tea applications [1][2]. Key details regarding this release include: - Version: v2.1.1 [1] - Published Date: July 4, 2026 [1] - Module Path: charm.land/bubbles/v2 [1] - Repository: The source code is maintained by Charmbracelet at github.com/charmbracelet/bubbles [1]. - License: MIT [1] - Go Version Requirement: Requires Go 1.25.0 or later [1]. This module provides various ready-to-use components for building terminal user interfaces (TUIs) in Go, such as: - stopwatch: A component for implementing stopwatch functionality [3]. - paginator: A tool for calculating and managing pagination [2]. - filepicker: A file selection component [4]. - key: Utilities for managing user-definable keybindings [5]. As of July 8, 2026, v2.1.1 is the latest default version of the module [1].

Citations:


🌐 Web query:

site:pkg.go.dev "charm.land/bubbletea/v2" "v2.0.8"

💡 Result:

The module charm.land/bubbletea/v2 at version v2.0.8 is a recognized version of the Bubble Tea framework, which is a popular library for building terminal user interfaces (TUIs) in Go [1][2][3]. It is widely used in various open-source projects and CLI tools as a dependency for creating modern, interactive terminal applications [2][4][3][5]. For official documentation and usage details regarding this module, you can access the Go package documentation at https://pkg.go.dev/charm.land/bubbletea/v2 [1].

Citations:


🌐 Web query:

site:pkg.go.dev "charm.land/lipgloss/v2" "v2.0.5"

💡 Result:

charm.land/lipgloss/v2 v2.0.5 refers to a specific version of the Lip Gloss library, a popular Go package used for defining styles and layouts in terminal applications [1]. Key points regarding this version: - Identity: It is the same library as the well-known charmbracelet/lipgloss, hosted under the charm.land domain, which serves as a module namespace for projects by Charm. This version (v2.0.5) is actively indexed and documented on pkg.go.dev [1]. - Functionality: The library provides tools for creating sophisticated terminal user interfaces (TUIs) [1]. It includes core styling capabilities, as well as sub-packages for specific UI components, such as list and tree [2][3]. - Usage: It is currently utilized as a dependency in various Go projects, including CLI tools and agents [4][5][6]. As of July 2026, version 2.0.5 is a stable release for developers building interfaces with Go [1].

Citations:


🌐 Web query:

site:pkg.go.dev "golang.org/x/sys" "v0.47.0"

💡 Result:

As of July 8, 2026, golang.org/x/sys v0.47.0 is not listed as the current or latest version of the module. The most recent established version for golang.org/x/sys noted in official documentation as of late May 2026 is v0.46.0 [1][2]. While other related Go sub-repositories (such as golang.org/x/net, golang.org/x/crypto, and golang.org/x/tools) have released versions labeled v0.47.0 [3][4][5], the golang.org/x/sys module has not reached that version number. You can verify the latest available version of the module by visiting the official package page at https://pkg.go.dev/golang.org/x/sys [2].

Citations:


🌐 Web query:

site:go.dev "go1.25.9" release

💡 Result:

Go 1.25.9 was released on April 7, 2026 [1]. This release included several important security fixes addressing vulnerabilities in the Go toolchain and standard library, specifically: - cmd/go: Addressed a security vulnerability (CVE-2026-27140) [2]. - crypto/tls: Fixed a vulnerability (CVE-2026-32283) affecting TLS handshake and connection handling [3]. - archive/tar: Resolved a memory allocation issue (CVE-2026-32288) related to reading malformed "old GNU sparse map" archives [4]. - cmd/compile: Addressed a compiler-related vulnerability (CVE-2026-27144) [5]. These fixes were part of a coordinated security release, and users of the Go 1.25 branch were encouraged to update to version 1.25.9 to mitigate these risks [1].

Citations:


🌐 Web query:

site:golang.org/dl/ "go1.25.9"

💡 Result:

There is no official record or release of a Go version 1.25.9 as of July 8, 2026. The official Go release documentation does not list this version [1].

Citations:


Bump golang.org/x/sys to a published release charm.land/bubbles/v2, charm.land/bubbletea/v2, and charm.land/lipgloss/v2 look fine, but golang.org/x/sys v0.47.0 doesn’t appear to be a real release, so this go.mod won’t resolve until that dependency is updated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go.mod` around lines 3 - 28, Update the go.mod dependency on golang.org/x/sys
because the current v0.47.0 version is not a published release and prevents
module resolution. Replace it with a valid released version that is compatible
with the existing charm.land/bubbles/v2, charm.land/bubbletea/v2, and
charm.land/lipgloss/v2 dependencies, then verify the module graph still resolves
cleanly.

Comment thread internal/session/codex.go
Comment on lines +46 to +57
// Delete shells out to the codex CLI so codex can keep its own bookkeeping
// (history metadata) consistent instead of us unlinking the rollout file.
func (codexProvider) Delete(row Row) error {
command := exec.Command("codex", "delete", "--force", row.ID)
if info, err := os.Stat(row.CWD); err == nil && info.IsDir() {
command.Dir = row.CWD
}
if output, err := command.CombinedOutput(); err != nil {
return fmt.Errorf("codex delete failed: %w: %s", err, strings.TrimSpace(string(output)))
}
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a timeout to the codex delete shell-out.

command.CombinedOutput() blocks indefinitely if the codex CLI hangs (e.g. stalls on a network/auth check despite --force). There's no way to recover short of killing the whole app.

🕐 Suggested fix using exec.CommandContext with a timeout
+import "context"
+import "time"
+
 func (codexProvider) Delete(row Row) error {
-	command := exec.Command("codex", "delete", "--force", row.ID)
+	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+	defer cancel()
+	command := exec.CommandContext(ctx, "codex", "delete", "--force", row.ID)
 	if info, err := os.Stat(row.CWD); err == nil && info.IsDir() {
 		command.Dir = row.CWD
 	}
 	if output, err := command.CombinedOutput(); err != nil {
 		return fmt.Errorf("codex delete failed: %w: %s", err, strings.TrimSpace(string(output)))
 	}
 	return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Delete shells out to the codex CLI so codex can keep its own bookkeeping
// (history metadata) consistent instead of us unlinking the rollout file.
func (codexProvider) Delete(row Row) error {
command := exec.Command("codex", "delete", "--force", row.ID)
if info, err := os.Stat(row.CWD); err == nil && info.IsDir() {
command.Dir = row.CWD
}
if output, err := command.CombinedOutput(); err != nil {
return fmt.Errorf("codex delete failed: %w: %s", err, strings.TrimSpace(string(output)))
}
return nil
}
// Delete shells out to the codex CLI so codex can keep its own bookkeeping
// (history metadata) consistent instead of us unlinking the rollout file.
func (codexProvider) Delete(row Row) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
command := exec.CommandContext(ctx, "codex", "delete", "--force", row.ID)
if info, err := os.Stat(row.CWD); err == nil && info.IsDir() {
command.Dir = row.CWD
}
if output, err := command.CombinedOutput(); err != nil {
return fmt.Errorf("codex delete failed: %w: %s", err, strings.TrimSpace(string(output)))
}
return nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/session/codex.go` around lines 46 - 57, The codexProvider.Delete
shell-out can block forever because command.CombinedOutput has no timeout;
update Delete to use exec.CommandContext with a timeout-aware context so a hung
codex CLI can be canceled. Create the command from the context before setting
command.Dir, keep the existing cwd behavior and error wrapping, and make sure
the timeout is applied specifically around the codex delete invocation in
codexProvider.Delete.

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