Skip to content

Plugin tools should respect metadata title in TUI display #18585

Closed as not planned

Description

@jturmel

Problem

Plugin tools always display as toolname [args] or toolname JSON.stringify(input) in the TUI, even when the tool sets a custom title via context.metadata({ title: "..." }).

Expected Behavior

When a plugin tool calls context.metadata({ title: "custom title" }), the TUI should display the custom title instead of the raw tool name + args.

For example, a go-task plugin tool named task_run that sets:

context.metadata({ title: "test" })

Should display as:

task_run test

Instead of:

task_run [task=test]

Or ideally, allow the title to fully replace the display:

task test

Current Behavior

The TUI ignores the state.title set by context.metadata() and always shows the tool name + JSON-stringified input args.

Context

Looking at the source:

  • packages/opencode/src/cli/cmd/run.ts has a fallback() function that checks state.title
  • However, this appears to be for CLI mode, not the interactive TUI
  • Built-in tools like bash, read, glob have custom rendering that shows clean output
  • Plugin tools fall through to generic rendering

Proposed Solution

  1. In the TUI, check state.title for plugin tools and use it when present
  2. Consider allowing plugins to fully override the display (just show title, not toolname title)

Environment

  • OpenCode version: 1.2.27
  • Platform: Linux

Use Case

Building a go-task plugin that wraps task commands. Want task_run to display as task test rather than task_run [task=test] for a cleaner UX that matches how the underlying command looks.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 22, 2026
  2. github-actions commented on Mar 22, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  3. gotgenes commented on Mar 30, 2026

    @gotgenes

    Additional context: there's an asymmetry in how state.title is consumed between the parent session view and the subagent's own view.

    The Task component in the TUI (packages/opencode/src/cli/cmd/tui/routes/session/index.tsx) does read state.title from the subagent's tool parts and renders it as a live subtitle:

    Pre-Completion-Reviewer Task — Re-run pre-completion review
      ↳ Ci_watch [0/1] ci — in_progress (30s)
    

    But if you navigate into the subagent session, the same ci_watch tool part renders via GenericTool → InlineTool, which ignores state.title entirely and just shows the static input args:

    ⚙ ci_watch [workflow=ci, run_id=23768861581, timeout=300]
    

    So the data is there and flowing correctly — context.metadata({ title }) writes to state.title, the message.part.updated event fires, and the reactive store updates. The Task component reads it. But GenericTool/InlineTool don't.

    The fix would be in GenericTool (or InlineTool more broadly) to check for state.title on running parts and display it — either replacing or supplementing the raw input display.

  4. assigned and unassigned on Apr 26, 2026
  5. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  6. malhashemi commented on May 29, 2026

    @malhashemi
    Contributor

    Hit this same wall building a workflow orchestrator plugin that fans out subagents via session.create({ parentID }). Confirming it's broader than the title — the whole renderer is gated on the tool name, in both frontends:

    • TUI (cli/cmd/tui/feature-plugins/system/session-v2.tsx): a <Switch> on props.part.name (task → the rich <Task> card, everything else → <GenericTool>), and GenericTool reads neither state.title nor state.metadata.
    • Web (packages/ui/src/components/message-part.tsx): ToolRegistry.render(part().tool) plus three if (part().tool !== "task") return guards.

    Since a plugin tool's name is fixed to its Hooks.tool key, nothing it emits can reach the task renderer. ctx.metadata already reaches the running part (session/tools.ts:50-63), so this is purely a renderer-selection gate, not a data gap.

    Minimal fix that also resolves #20184: select the subagent renderer by a metadata marker rather than the literal name — route any tool part whose state.metadata carries sessionId (or kind:"subagent") to the existing task widget, with task setting the marker itself. No new render API, task behaviour unchanged, and any plugin opts in via the existing ctx.metadata channel. (A sessionIds[] variant would let one part represent a fan-out of several children.)

  7. github-actions commented on Jul 29, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions