Skip to content

feat: Integrate plugin commands from upstream PR #4411 #132

Description

@shuv1337

Summary

Integrate the plugin commands feature from upstream PR sst/opencode#4411 which allows plugins to register custom slash commands that execute code.

Context

The upstream PR adds a powerful new plugin.command hook that enables plugins to register their own /commands in the TUI. This unlocks use cases like:

  • /prune - prune tool outputs from session
  • Toggling plugin features on/off
  • Creating composable workflows
  • Custom session manipulation commands

Acceptance Criteria

  • Plugin commands appear in autocomplete when typing /
  • Plugin commands with sessionOnly: true only show when a session exists
  • Command aliases work (e.g., /hi resolves to /hello)
  • Plugin command execution works with error handling
  • SDK types are updated with sessionOnly and aliases fields
  • SDK rebuild works (cd packages/sdk/js && bun run script/build.ts)

Implementation Details

Files to Update

File Changes
packages/plugin/src/index.ts:145-192 Add plugin.command hook interface to Hooks
packages/opencode/src/plugin/index.ts:53-76 Exclude plugin.command from trigger, add client() export
packages/opencode/src/command/index.ts:23-79 Add sessionOnly/aliases to schema, load plugin commands, support alias lookup
packages/opencode/src/session/prompt.ts:1413-1498 Execute plugin commands with error handling
packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx:207-222 Filter sessionOnly commands, add aliases to options
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx:506-518 Resolve command aliases
packages/sdk/js/src/gen/types.gen.ts Add sessionOnly to Command type
packages/sdk/js/src/v2/gen/types.gen.ts Add sessionOnly and aliases to Command type

Key Changes

1. New Plugin Hook Interface (packages/plugin/src/index.ts)

"plugin.command"?: {
  [key: string]: {
    description: string
    aliases?: string[]
    sessionOnly?: boolean
    execute(input: { sessionID?: string; client: ReturnType<typeof createOpencodeClient> }): Promise<void>
  }
}

2. Command Schema Updates (packages/opencode/src/command/index.ts)

// Add to Command.Info schema
sessionOnly: z.boolean().optional(),
aliases: z.array(z.string()).optional(),

3. Plugin Command Loading (packages/opencode/src/command/index.ts)

const plugins = await Plugin.list()
for (const plugin of plugins) {
  const commands = plugin["plugin.command"]
  if (!commands) continue
  for (const [name, cmd] of Object.entries(commands)) {
    if (result[name]) continue
    result[name] = {
      name,
      description: cmd.description,
      template: "",
      sessionOnly: cmd.sessionOnly,
      aliases: cmd.aliases,
    }
  }
}

4. Plugin Command Execution (packages/opencode/src/session/prompt.ts)

  • Check for plugin commands before built-in commands
  • Execute with { sessionID, client } context
  • Handle errors gracefully with user feedback

Example Plugin Command

import type { Plugin } from "@opencode-ai/plugin"

export const CommandsPlugin: Plugin = async (ctx) => {
  return {
    "plugin.command": {
      hello: {
        description: "Say hello",
        aliases: ["hi"],
        sessionOnly: true,
        async execute({ sessionID, client }) {
          await client.session.sendMessage({
            path: { id: sessionID! },
            body: { content: "Hello from plugin command!" },
          })
        },
      },
    },
  }
}

Tasks

  • Cherry-pick or manually apply changes from upstream PR
  • Verify no conflicts with fork-specific changes
  • Rebuild SDK: cd packages/sdk/js && bun run script/build.ts
  • Test plugin command registration
  • Test autocomplete with sessionOnly filtering
  • Test alias resolution
  • Test error handling for failed plugin commands
  • Run full test suite

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions