feat(web): composer bang grammar library + terminal_command receipt field plumbing#3034
Draft
btli wants to merge 2 commits into
Draft
feat(web): composer bang grammar library + terminal_command receipt field plumbing#3034btli wants to merge 2 commits into
btli wants to merge 2 commits into
Conversation
…bing Add the client-side grammar for composer `!` shell commands and thread the bang-receipt fields through the terminal_command render pipeline. Pure library/plumbing code — composer submit wiring and the autocomplete menu land in a follow-up, so nothing is user-reachable yet. - web/src/lib/composerBang.ts: framework-free grammar (mirrors composerMentions): `! cmd` (new default-type shell), `!<key> cmd` (send into a running shell), `!<type> cmd` (new shell of that type), bare `!` / focus-only forms, `\!` + leading-space escapes. Exact-token resolution: running-shell session key → full resource id → declared type → error; dead shells and agent panes are never targetable. - Receipt fields (action, terminal_id, terminal_name, session_key, status, error) plus item-level created_by threaded through sse.ts, events.ts, blockStream.ts, blocks.ts, itemsToBlocks.ts, conversationItems.ts, renderItems.ts, BlockRenderer.tsx. The delivery outcome rides the item-level status slot (the server flattens the data payload over the item envelope), so only "ok"/"error" surface. - TerminalCommandCard: target-shell chip (`→ zsh · u-ab12cd`, spawn variant `→ new …`), destructive styling + message line for error receipts, optional click-to-focus hook for the shell rail. Legacy no-`action` items render byte-identically (snapshot-pinned against the pre-change markup). Tests: exhaustive grammar table for composerBang; card variant snapshots; field passthrough in the sse/blockStream/itemsToBlocks/ renderItems suites. Signed-off-by: Bryan Li <bryan.li@gmail.com> Co-authored-by: omnigent <noreply@omnigent.ai>
Three blocking review fixes against the spec's grammar and receipt contracts: - Whitespace-only commands are preserved verbatim: only a truly empty remainder after the single separator counts as "no command", so `!u-ab12cd ` sends the literal spaces instead of collapsing to a focus action. The grammar test that pinned the trimming behavior is corrected, with new coverage for tab separators, tab-only and Unicode-whitespace-only commands, the literal `u-` token, and the no-command running-key-vs-type precedence collision. - Receipt status is gated on the `action` discriminator via one shared `terminalCommandStatus(action, status)` rule used by both the SSE and reload chokepoints, and the card ignores status/error entirely without `action` — a legacy item carrying a flattened "ok"/"error" status now provably renders byte-identically to plain legacy (new equality + snapshot tests). - Error receipts draw a destructive border in addition to the icon and message line, per the receipt rendering contract. Signed-off-by: Bryan Li <bryan.li@gmail.com> Co-authored-by: omnigent <noreply@omnigent.ai>
Contributor
Author
|
Cross-review fixes pushed in 5268910:
Deferred as agreed: forwarding 🤖 Generated with Claude Code |
13 tasks
Contributor
|
@btli This PR is a Bug fix, Feature, or UI / frontend change but the Demo section is missing or only contains a placeholder. These change types require a screenshot or screen recording so reviewers can see the new behaviour without checking out the branch. Please update the Demo section with:
Use |
btli
marked this pull request as draft
July 22, 2026 02:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
N/A — task T3 of the composer bang-shell-commands workstream (grammar + receipt plumbing; sibling backend PRs: runner input route #3033).
Summary
Client-side groundwork for Claude Code-style
!shell commands in the chat composer. Pure library/plumbing code — the composer submit wiring and autocomplete menu land in a follow-up PR, so nothing here is user-reachable yet:web/src/lib/composerBang.ts— framework-free bang grammar (mirrorscomposerMentions.ts):! cmd(new default-type shell),!<running-key> cmd(send into an existing shell),!<shell-type> cmd(new shell of that type), bare!/ focus-only forms, and the\!+ leading-space escape hatches. Resolution is exact-token, first match wins: running-shell session key → full resource id → declared type → inline error. Dead shells and agent panes are never targetable.terminal_commandreceipt fields (action,terminal_id,terminal_name,session_key,status,error) plus item-levelcreated_bythread throughsse.ts→events.ts→blockStream.ts/itemsToBlocks.ts→blocks.ts→renderItems.ts→BlockRenderer.tsx. The delivery outcome rides the item-levelstatusslot (the server flattens the data payload over the item envelope), so only"ok"/"error"surface as outcomes and lifecycle statuses stay invisible.TerminalCommandCard— receipts grow a muted target-shell chip (→ zsh · u-ab12cd, spawn variant→ new zsh · u-ab12cd), destructive icon + message line onstatus="error", and an optionalonFocusShellcallback so the follow-up can wire click-to-focus of the shell rail. Legacy no-actionitems (the Claude Code TUI observer path) render byte-identically — pinned by snapshots recorded against the pre-change markup.ELI5: we taught the web app the
!commandmini-language and how to display "this command was sent to that shell" receipts, but haven't plugged either into the message box yet.Test Plan
All from
web/on this branch:bun run lint(oxlint) — no new findings; the 10 pre-existing errors onmain(unrelated files) are unchanged.bun run type-check(tsc -b) — passed.bunx vitest run src/lib/composerBang.test.ts src/lib/sse.test.ts src/lib/blockStream.test.ts src/lib/itemsToBlocks.test.ts src/lib/renderItems.test.ts src/components/blocks/TerminalCommandCard.test.tsx src/components/blocks/BlockRenderer.test.tsx— 7 files, 235 passed.bun run test— identical pass/fail profile to cleanmainon this machine (646 pre-existing local-env failures on both, +54 new passing tests, 0 new failures).pre-commit run --all-files— passed.Demo
N/A — non-visual plumbing behind dead code; legacy rendering is snapshot-pinned unchanged, and the new receipt variants only render once the server-side
shell_commandevent (separate PR) starts emitting the fields.Note on the e2e-ui gate: this touches
web/**but adds no reachable UI behavior; the Playwrighttests/e2e_uicoverage for the bang-command flow lands with the follow-up composer-integration task (or the maintainerskip-e2e-ui-testlabel applies to this plumbing-only PR).Type of change
Test coverage
Coverage notes
Exhaustive vitest table for every grammar row (escapes, verbatim multiline commands, precedence/collisions, dead shells, agent panes, no-shell-access) plus snapshot tests for all card variants and field-passthrough tests at each plumbing chokepoint. E2E UI coverage intentionally deferred to the follow-up task that makes the feature reachable (see Demo note).
🤖 Generated with Claude Code