anomalyco / anomalyco/opencode

feat(tui): expose stable prompt action commands to plugins

Open
#40,953 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

2.0 enhancement tui
Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Problem

OpenCode V2 form and permission prompts own the correct selection, validation, and submission behavior, but their navigation and primary actions are registered as anonymous inline key bindings.

TUI plugins can dispatch named commands through context.keymap.dispatch(id) and inspect reachable named commands through context.keymap.commands(). Inline bindings do not become command entries, so plugins cannot invoke the active prompt semantically.

A concrete TP-7 hardware integration attempted to map its reel to option navigation and its Plus button to selection/confirmation. Looking for reachable Form or Permission bindings in keymap.commands() always returned nothing, so the plugin fell back to normal scrolling.

Relevant code:

  • packages/tui/src/routes/session/form.tsx: form option navigation and Return actions are inline bindings.
  • packages/tui/src/routes/session/permission.tsx: permission option navigation and Return are inline bindings.
  • packages/tui/src/context/keymap.tsx: inline bindings are registered directly, while useCommands() exposes named command entries only.

Proposed Seam

Give prompt actions stable semantic command IDs while keeping prompt components authoritative for state and behavior. For example:

form.option.previous
form.option.next
form.action

permission.option.previous
permission.option.next
permission.action

The exact names are open to refinement. The important property is that the active prompt layer exposes reachable named commands that plugins can dispatch. Existing keyboard bindings should continue to invoke the same component-owned handlers.

A plugin could then select only a reachable action and preserve fallback behavior outside prompts:

const command = context.keymap.commands().find((item) =>
  item.id === "form.option.next" || item.id === "permission.option.next"
)
if (command?.id) context.keymap.dispatch(command.id)
else context.keymap.dispatch("session.line.down")

Why This Seam

The prompt component should continue to own:

  • Current selection and field state
  • Conditional fields and multiselect behavior
  • Validation
  • External-action acknowledgement
  • Reply, cancellation, and submission

Hardware integrations and other plugins should express semantic intent through keymap commands. They should not read prompt data and maintain duplicate UI state, call form APIs directly, search commands by display title, or inject synthetic keyboard events.

This also supports accessibility adapters, macros, and future hardware controllers without coupling them to form internals.

Acceptance Criteria

  • Active form option navigation is reachable and dispatchable through stable named command IDs.
  • The active form primary Return action is reachable and dispatchable through a stable named command ID across option, review, external, and text-edit states.
  • Active permission option navigation and selection are reachable and dispatchable through stable named command IDs.
  • Commands are reachable only when their owning prompt layer is active.
  • Existing keyboard behavior and bindings remain unchanged.
  • Tests prove a plugin-facing command lookup can discover and invoke these actions.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/tui/src/context/keymap.tsx, then inspect the inline bindings in packages/tui/src/routes/session/form.tsx and permission.tsx. Trace how useCommands() and context.keymap.dispatch() expose commands, and identify the existing prompt states and handlers. Done means active prompt actions are discoverable and dispatchable in tests while existing keyboard behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, testing, tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.