Skip to content

feat: UX parity with Claude Code (5 improvements)#19360

Closed
johnnykang101 wants to merge 68 commits intoanomalyco:devfrom
CobuilderLabs:feat/ux-parity-claude-code
Closed

feat: UX parity with Claude Code (5 improvements)#19360
johnnykang101 wants to merge 68 commits intoanomalyco:devfrom
CobuilderLabs:feat/ux-parity-claude-code

Conversation

@johnnykang101
Copy link
Copy Markdown

Summary

Closes the UX gap between CoBuilder and Claude Code across 5 targeted improvements, all additive and non-breaking.

Changes

# Improvement File(s)
1 Edit tool parts expanded by default — write/edit tool blocks now open on render, matching Claude Code's behavior packages/app/src/context/settings.tsx
2 Diffs open by default — the modified-files collapsible at the bottom of each turn starts open, making changes immediately visible packages/ui/src/components/session-turn.tsx
3 Contextual bottom HUD — a slim shortcut hint bar below the prompt input; idle shows / commands · @ mention, streaming shows Esc · to stop packages/app/src/pages/session/composer/session-composer-region.tsx
4 Interrupt discoverabilityEsc hint is now surfaced in the HUD during generation (Esc already worked; this makes it discoverable) packages/app/src/i18n/en.ts
5 Per-turn token/cost display↑ 1.2K ↓ 3.4K tokens · ~$0.0041 shown after each completed assistant turn packages/ui/src/components/session-turn.tsx

Notes

  • All settings that were changed (edit tool expanded, diffs open) remain user-configurable via the existing settings toggles
  • Token/cost display respects existing data from AssistantMessage.tokens and .cost — no backend changes
  • HUD is aria-hidden and pointer-events-none — purely informational, no interaction required
  • 4 files, +75 lines total

Planning artifacts

  • Context: .planning/phases/ux-parity/CONTEXT.md
  • Plan: .planning/phases/ux-parity/PLAN.md

johnnykang101 and others added 30 commits March 26, 2026 09:43
- Add 9Router as a named ProviderID in schema.ts (9router)
- Add 9Router to popularProviders list in use-providers.ts
- New DialogConnect9Router: pre-filled form (baseURL + JWT token)
  with Claude Sonnet/Opus/Haiku defaults via openai-compatible SDK
- Update DialogSelectProvider: 9Router appears as a static "popular"
  entry with a "Local" tag, routes to the dedicated connect dialog
- New DialogOnboarding: welcome wizard shown on first launch when no
  providers are connected; leads directly to provider selection
- OnboardingCheck in RouterRoot: detects zero connected providers on
  mount and shows the onboarding dialog (localStorage-gated)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove API key field entirely. 9Router is a local container that
doesn't require auth — connection is just a baseURL. A dummy apiKey
is set in options only to satisfy the openai-compatible SDK constructor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fetch /v1/models from the running 9Router container, show a
checklist of available models (all selected by default), and
register only the ones the user picks. Includes select/deselect all
toggle and a count summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Single button now tests the connection and fetches models in one step.
On success: green dot + "Connected — N models available" + checklist.
On failure: red dot + inline error message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add useGlobalSync import to app.tsx (used in OnboardingCheck)
- Explicitly type all new Set() calls as Set<string> to satisfy
  TypeScript inference in dialog-connect-9router.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Interactive terminal wizard (opencode onboard) for first-run setup.
Supports 9Router (URL + model picker) and API key providers
(Anthropic, OpenAI, OpenRouter, Google).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If enabled_providers exists in config, 9router was being filtered out.
Now onboard adds it to the list automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CI: typecheck on every PR targeting main
- CD: auto-release on merge to main with semver bump and release notes
  - feat commits → minor bump
  - fix/chore → patch bump
  - breaking change → major bump

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Syncs anomalyco/opencode dev → our dev branch every Monday at 6am UTC.
- Clean merge: auto-pushes to dev
- Conflicts: opens a PR for manual resolution

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…isions

Tags now follow cb-vX.Y.Z pattern (e.g. cb-v0.1.0) so they never
conflict with upstream anomalyco/opencode tags (v0.0.45, v1.4.0, etc).
Release titles display as 'CoBuilder vX.Y.Z' for clarity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CLI command renamed from opencode → cobuilder
- package.json version reset to 0.1.1 (CoBuilder versioning)
- build.ts: output binary named cobuilder, uploads to cb-vX.Y.Z tags
- cd.yml: builds platform binaries (linux/darwin/windows x64/arm64)
  and attaches them to each release
- install.sh: one-line curl install with platform detection,
  PATH setup, and automatic onboarding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces --generate-notes (PR-only) with categorized commit log.
Notes are grouped into Features / Bug Fixes / Maintenance sections
and link Full Changelog between our own cb-v* tags only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Strip conventional commit prefixes from user-visible notes
- Capitalize entries automatically
- Sections: Added / Fixed / Changed / Security / Breaking Changes
- Include release date in header
- Full Changelog link scoped to cb-v* tags only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Security layers added (TypeScript/Bun adaptations):
- SSRF protection: blocks cloud metadata, private IPs, localhost by default
- Prompt injection scanner: detects override/jailbreak/exfil patterns
- Path traversal prevention: canonicalize + base-escape detection
- Token bucket rate limiter: per-key cost-aware limiting
- Merkle-chained audit log: tamper-evident append-only event log
- HTTP security headers: CSP, HSTS, X-Frame, X-Content-Type

CI hardened with:
- Lint job
- Dependency audit (bun audit)
- Secret scanning (TruffleHog --only-verified)
- SAST (CodeQL for TypeScript)
- Unit tests (bun test) with security module coverage

Inspired by OpenFang's 16-layer defense-in-depth architecture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
build.ts uses 'gh release upload cb-vX.Y.Z' which requires the release
to already exist. Previously we created it after the build — reversed order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Persist active session to checkpoint.json on each user message.
On next launch, detect interrupted sessions and offer to resume.
Checkpoint clears on clean exit (code 0) so it only exists after crashes.

Inspired by Claude Code's auto-dream session continuity pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Script.version fetches from opencode-ai npm registry (unavailable for
our fork) and falls back to '0.0.0-main-timestamp'. This caused
'gh release upload cb-v0.0.0-main-...' to target a non-existent release.

Now pass COBUILDER_RELEASE_TAG=cb-vX.Y.Z from CD workflow so build.ts
uploads to the correct release tag regardless of Script.version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inspired by Claude Code's auto-dream memory consolidation:

- SQLite FTS5 memory store: per-session summaries indexed for search
- Deterministic summarization: extracts user requests + tool actions
- Memory injection: top 3-5 relevant past sessions prepended to system prompt
- Lifecycle hook: auto-summarize when session is archived/closed
- Project-scoped: memories are per working directory

Memory hierarchy:
  Working memory (context window)
    → Session memory (SQLite messages, persisted)
      → Long-term memory (FTS5-searchable summaries, injected at next start)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ll guide

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lanations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… avatar style

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the >_ terminal box with the pixel-art Jindo mascot, clipped
to the same rounded square with a gradient border that ties into the
CoBuilder wordmark. README updated to use a single unified logo image.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
johnnykang101 and others added 25 commits March 26, 2026 15:14
* feat(04-01): add Step N of 4 progress prefixes to onboarding wizard

- Step 1 of 4 — Choose your provider (before provider select)
- Step 2 of 4 — Security modules (was: Security modules — configure which are active)
- Step 3 of 4 — Workflow plugins (optional) (was: Workflow plugins — install methodology tooling)
- Step 4 of 4 — Recommended tools (optional) (was: Recommended tools — extend CoBuilder)

* feat(04-03): background update download with persistent badge

- Replace confirm-dialog+exit flow with silent background download
- Add pendingUpdate signal set on successful upgrade
- Show toast on download start and completion
- Show persistent badge 'Update vX.Y.Z ready — restart to apply' at top
- Remove exit() call after update — user stays in app

* chore(04-03): remove unused DialogAlert and DialogConfirm imports

* feat(04-02): add first-run hint block to TUI home screen

- Added Show block with 3 contextual hints beneath the prompt
- Hints only visible when isFirstTimeUser() (zero sessions)
- Uses theme.primary for bullet markers and theme.text for emphasis
- Existing Tips component untouched

* docs(04-03): complete non-disruptive update flow plan

- 04-03-SUMMARY.md created
- STATE.md updated to phase 4, plan 3 complete

* feat(04-02): add Getting Started panel to Web home screen

- Replaced minimal empty state with numbered 3-step Getting Started panel
- Step numbers use rounded circles with bg-surface-raised-base
- Added home.gettingStarted.{title,step1,step2,step3} i18n keys
- All 5 locale files updated: en, es, zh, zht, no

* feat(04-01): enhance ConnectionError with elapsed timer and actionable guidance

- Add elapsedSeconds signal incremented every second in existing retry interval
- Replace static 'Retrying...' with 'Retrying... Ns' via app.server.retryElapsed key
- Add serverUrl memo extracting http.url from current server connection
- Show 'Try running: cobuilder serve' when no URL is known
- Show 'Check that the server is running at <url>' when URL is known
- Add app.server.retryElapsed, hint.serve, hint.url keys to all 17 locale files
- Fix HttpBase type error: use current.http.url (string) not current.http

* docs(04-02): complete first-run guidance plan

- Add 04-02-SUMMARY.md with task commits and deviation notes
- Update STATE.md: plan 02 complete, deferred issues logged

* docs(04-01): complete onboarding-progress and connection-error-elapsed plan

- 04-01-SUMMARY.md: step progress + ConnectionError elapsed timer
- STATE.md: updated stopped_at, added 2 decisions

* docs: mark Phase 4 UI/UX Polish II complete with verification

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: commit planning artifacts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Renames packages/opencode/bin/opencode → bin/cobuilder
- Updates bin wrapper: .cobuilder cache, cobuilder- asset prefix, cobuilder binary name
- Updates package.json bin field: ./bin/opencode → ./bin/cobuilder
- Fixes install.sh: opencode-${PLATFORM}-${ARCH} → cobuilder-${PLATFORM}-${ARCH}
- Adds `cobuilder` script to root package.json for `bun run cobuilder`

Without this, `cobuilder install` downloaded the right file but unpacked
looking for an `opencode` binary — and the install script was fetching
opencode-* asset names that no longer exist.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The security headers middleware called Config.get() after next(), but early
TUI worker requests arrive before the instance AsyncLocalStorage context is
initialised, causing an UnknownError crash on every startup.

Fix: wrap in try/catch and apply headers unconditionally when config is not
yet available — which is the secure default.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…target

After renaming the binary prefix from "opencode" to "cobuilder", the
target string was no longer replaced correctly since pkg.name is still
"opencode". Bun requires compile targets to start with "bun-", so
cobuilder-linux-arm64 caused a TypeError and broke every CD build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the script is piped (curl | bash), stdin is a pipe not a TTY,
so @clack/prompts can't read arrow keys — they print as raw escape
sequences. Redirect stdin from /dev/tty when available so interactive
onboarding works seamlessly from the one-liner install command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- bin/cobuilder: OPENCODE_BIN_PATH → COBUILDER_BIN_PATH env var
- pr.ts: spawn cobuilder binary instead of opencode (runtime fix)
- uninstall.ts: brew/choco/scoop package names, intro/outro text
- run.ts/worker.ts/plugin/index.ts/server.ts: default auth username

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…repos

gsd-workflow, ralph-loop-workflow, and gstack-workflow repos do not exist
under CobuilderLabs. Keeping them caused a confusing git clone failure.
Users should provide a full GitHub URL until real workflow repos are published.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Repo now exists at https://github.com/CobuilderLabs/gsd-workflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors how Claude Code installs skills — clones the repo and drops
command markdown files into ~/.config/opencode/commands/<skill>/
so they are available as /<skill>:<command> slash commands.

Usage: cobuilder skills install gsd

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neither repo uses a compatible command structure for CoBuilder skills.
Only GSD (CobuilderLabs/gsd-workflow) is installable. Remove the others
to avoid offering options that would silently fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CoBuilder's AI was checking ~/.claude/skills/ when asked about installed
tools. Add installedCommands() to SystemPrompt so the AI knows commands
live at ~/.config/opencode/commands/ and can correctly report what's installed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…so binaries show proper semver

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ing latest

CD creates the release before uploading binaries, so the "latest" tag
can briefly have no assets. Walk up to 10 recent releases and pick the
newest one that actually has the expected asset file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oaded

Prevents the "latest" tag from pointing to an empty release during the
build window. Also adds branch protection context for PRs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nd release

HTTP HEAD probing is unreliable (GitHub uses 301/302/other codes).
Query each release's asset list via the API to reliably find the newest
non-draft release that has the expected binary for the user's platform.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents electron-builder from auto-publishing in CI (it detects CI env
and tries to publish, failing without GH_TOKEN). The explicit gh release
upload step handles all artifact publishing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x makes this reliable

The CD pipeline now publishes releases only after all binaries are
uploaded (draft → publish on success). No scanning needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TUI (app.tsx) uses React JSX but react was missing from the package
dependencies, causing 'Cannot find module react/jsx-dev-runtime' on
bun run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/dev/tty may exist as a device node in containers but fail to open.
Use a subshell exec test to confirm it's accessible before using it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(exec </dev/tty) leaks bash errors; { true </dev/tty; } 2>/dev/null
cleanly tests if the device can be opened without any output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
true </dev/tty passes even when the device is non-functional because the
open() syscall succeeds but reads fail. stty requires an actual terminal
and correctly fails in headless containers, falling through to the
manual instructions path.

Also adds test-e2e.sh: install → version → commands → skills → uninstall → reinstall

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#18)

* fix(onboard): replace multi-select model input with single select

Users select one default model during onboard — they can switch to any
other model from the same provider inside the app. The comma-separated
number input was confusing and unnecessary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(onboard): restore security step, single-select model, fix instance crash

- Remove Config.get() call that crashed with "No context found for instance"
  during onboarding (instance ALS not initialized in CLI context)
- Replace comma-separated multi-select model input with single select
- Restore security modules step (inline, no missing module deps)
- Move prompts.outro to main handler after all steps complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tips): rebrand all opencode references to cobuilder

Replace opencode CLI commands, /opencode slash commands, OpenCode product
name, and .opencode/ config paths with their cobuilder equivalents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(system-prompt): tell model not to use which/shell to check for skills

Skills are CoBuilder-native and accessed via the skill tool, not system
binaries. Without this hint models run `which gsd` and report it as
not installed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(system-prompt): list installed skills in env block

Adds 'Installed skills: gsd:plan-phase, ...' to the <env> section so
the model knows upfront what skills are available without needing to
run which/command -v shell checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(e2e): validate all onboard wizard selections — providers + security modules

Step 8 now covers both wizard steps:
- Step 1: all 5 providers (Anthropic, OpenAI, OpenRouter, Google, 9Router)
- Step 2: all 6 security modules — each individually disabled, all disabled,
  and combined provider+security state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompt): rebrand OpenCode→CoBuilder, add mandatory GSD workflow

CoBuilder is the product name throughout the system prompt. Added
mandatory GSD section so the model proactively routes non-trivial
tasks through the structured workflow without user having to ask.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompt): broaden CoBuilder identity beyond coding agent

CoBuilder builds software, products, automations, content, plans,
and more — not just code. Update system prompt to reflect this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompt): rebrand all model prompts — CoBuilder is a general AI agent

Update all 6 remaining prompt files (default, gemini, gpt, beast,
trinity, codex) to use CoBuilder identity and broaden scope beyond
software engineering to "build anything".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add --autopilot flag to auto-approve all permission requests

Adds `cobuilder --autopilot` (or env var COBUILDER_AUTOPILOT=1) which
skips permission confirmation prompts — useful for CI, scripts, and
non-interactive runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tui,prompt): cleaner tool output + autonomous GSD behavior

- TUI: show only first input value (truncated to 60 chars) instead of
  dumping all key=value pairs for generic/MCP tool calls
- Prompt: remove gsd:do/sub-skill references that caused double skill
  load; instruct model to complete full GSD flow autonomously without
  asking follow-up questions when intent is clear

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tui): show autopilot badge in prompt mode line

When --autopilot is active, displays "autopilot ·" in warning color
next to the agent name in the prompt area so the user can see at a
glance that permission prompts are being auto-approved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(provider): strip trailing assistant messages for Claude models

claude-sonnet-4-6 and newer Claude models reject requests where the last
message is an assistant role ("assistant message prefill"). Strip any
trailing assistant messages before sending to Anthropic/Claude endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…g instance context (#19)

The security headers middleware called Config.get() after next(), but early
TUI worker requests arrive before the instance AsyncLocalStorage context is
initialised, causing an UnknownError crash on every startup.

Fix: wrap in try/catch and apply headers unconditionally when config is not
yet available — which is the secure default.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Open edit tool parts expanded by default (matches Claude Code behavior)
- Open diffs collapsible by default after each completed turn
- Add contextual bottom HUD showing shortcuts below the prompt input
  (idle: '/ commands · @ mention', streaming: 'Esc · to stop')
- Improve interrupt discoverability by surfacing Esc hint during generation
- Add per-turn token/cost display after each assistant response
@github-actions
Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Mar 27, 2026
@johnnykang101
Copy link
Copy Markdown
Author

Wrong repo — PR created in CobuilderLabs/opencode instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:compliance This means the issue will auto-close after 2 hours.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant